W tym artykule zajmiemy się rozbudową samego modułu wiadomości. Dodamy kategorie oraz kilka dodatkowych pól. Jeżeli chodzi o kategorie to możemy zrobić prosty system - dana wiadomość w jednej kategorii (Many-To-One) lub skorzystać z relacji Many-To-Many by móc przypisać jedną wiadomość wielu kategoriom. By nie było za łatwo skorzystamy z tej drugiej możliwości. Oto jak wygląda model aplikacji "news" po zmianach:
# -*- coding: utf-8 -*-
from django.db import models
class NewsCategories(models.Model):
name = models.CharField(max_length=255, verbose_name='Nazwa Kategorii')
slug = models.SlugField(max_length=255, unique=True, prepopulate_from=("name", ), verbose_name='Odnośnik')
icon = models.ImageField(upload_to='icons', verbose_name='Ikonka Kategorii')
class Meta:
verbose_name = "Kategoria"
verbose_name_plural = "Kategorie"
class Admin:
list_display = ('name',)
def __str__(self):
return self.name
class News(models.Model):
category = models.ManyToManyField(NewsCategories, verbose_name='Kategorie')
title = models.CharField(max_length=255, verbose_name='Tytuł')
slug = models.SlugField(max_length=255, unique=True, prepopulate_from=("title", ), verbose_name='Odnośnik')
text = models.TextField(verbose_name='Treść')
date = models.DateTimeField(blank=True, verbose_name='Data dodania')
wykop = models.CharField(max_length=255, verbose_name='Wykop', blank=True)
class Meta:
verbose_name = "Wiadomość"
verbose_name_plural = "Wiadomości"
class Admin:
list_display = ('title', 'date')
list_filter = ['date']
search_fields = ['title', 'text']
date_hierarchy = 'date'
def __str__(self):
return self.title
class NewsComments(models.Model):
news = models.ForeignKey(News)
text = models.TextField(verbose_name='Treść')
author = models.CharField(max_length=255, verbose_name='Autor', blank=True)
class Meta:
verbose_name = "Komentarze"
verbose_name_plural = "Komentarze"
class Admin:
list_display = ('text', 'author')
def __str__(self):
return self.text
Zmianie uległy istniejące modele tak więc musimy usunąć istniejące tabele w bazie danych i stworzyć je ponownie (syncdb). W przypadku SQLite wystarczy usunąć plik z bazą danych. W powyższym kodzie pojawił się model kategorii wiadomości
NewsCategories zawierający trzy pola - nazwa, slug i ikona. Pole
SlugField będzie zawierało bezpieczną dla odnośników wersję nazwy kategorii (wykorzystamy ją do tworzenia ładnych odnośników). Pole na ikonkę to
ImageField, które będzie zapisywało przesłane grafiki w
site_media/icons. W modelu
News pojawiły się trzy pola - pole zależności ManyToManyField do kategorii, pole SlugField na odnośnik oraz opcjonalne pole tekstowe "wykop" przeznaczone na link do strony na wykop.pl z linkiem do newsa na naszej stronie (o co chodzi zobaczysz później).
Po utworzeniu tabel i uruchomieniu serwera deweloperskiego, w Panelu Admina powinniśmy zobaczyć coś takiego:
Możemy dodać kilka kategorii. Wpisując nazwę pojawi się od razu tekst w polu odnośnika (automat nie obsługuje w pełni poprawnie polskich znaków - nie daje żadnej litery):
Dodając wiadomość możemy wybrać pasujące kategorie:
Dodaj kilka wiadomości, kilka z nich przypisz do więcej niż jednej kategorii. Gdy już mamy przykładowe dane możemy zabrać się za aktualizację skryptu. Zaczniemy od odnośnika do szczegółowego widoku wiadomości. Obecnie jest to
/news/ID/. W
urls.py zmieniamy wyrażenie regularne:
(r'^news/(?P<slug>[\w\-_]+)/$', 'news.views.show_news'),
Odpowiednio zmieniamy kod widoku:
def show_news(request, slug):
news = News.objects.get(slug=slug)
# tworzymy manipulator
manipulator = NewsComments.AddManipulator()
if request.POST:
# formularz wysłany
data = request.POST.copy()
# dodajemy brakujące dane
data['author'] = str(request.user)
data['news'] = news.id
errors = manipulator.get_validation_errors(data)
# jeżeli nie ma błędów zapisz dane i odświerz stronę - przekieruj na ten sam url
if not errors:
new = manipulator.save(data)
return HttpResponseRedirect('/news/'+ str(slug) +'/')
else:
# formularz nie wysłany
errors = {}
data = {}
# formularz
form = forms.FormWrapper(manipulator, data, errors)
comments = NewsComments.objects.filter(news=news.id)
return render_to_response('show.html', {'news':news, 'form':form, 'comments':comments})
Zamiast po id wiadomośc identyfikujemy po odnośniku:
News.objects.get(slug=slug). Następnie w
list.html zmieniamy odnośnik do widoku z
/news/{{ new.id }}/ na
/news/{{ new.slug }}/ i gotowe.
Teraz wyświetlmy listę kategorii przy wiadomości na widoku szczegółowym. Do szablonu
show.html pod treścią wiadomości dodajemy:
<h3>{% for c in news.category.all %}
<a href="/news/cat/{{ c.slug }}/">{{ c }}</a>
{% endfor %}</h3>
news.category to pole określające powiązanie. By uzyskać listę wszystkich powiązanych obiektów (kategorii) trzeba dodać "
.all". Podobnie możemy zrobić dla listy wiadomości. W powyższym kodzie zastosowaliśmy odnośnik, który jeszcze nie istnieje:
/news/cat/{{ c.slug }}/ - taki odnośnik wykorzystamy do listowania wiadomości z danej kategorii. Musimy stworzyć widok i dodać odpowiednią regułę do urls.py. Oto widok:
def by_category(request, slug):
from django.views.generic.list_detail import object_list
cat = NewsCategories.objects.get(slug=slug)
news = cat.set.all().order_by('-id')
return object_list(request, news, paginate_by = 5, allow_empty = True, template_name = 'category_list.html', extra_context={'cat': slug})
A reguła w
urls.py:
(r'^news/cat/(?P<slug>[\w\-_]+)/?$','news.views.by_category'),
A szablon:
{% extends "index.html" %}
{% block content %}
{% if object_list %}
{% for new in object_list %}
<h3>{{ new.title }}</h3>
<p>{{ new.text }}<br />{{ new.date|truncatewords:"1" }} | <b><a href="/news/{{ new.slug }}/">Komentarze</a></b>: {{ new.newscomments_set.count }} | <b>Kategorie</b>: {% for c in new.category.all %}<a href="/news/cat/{{ c.slug }}/">{{ c }}</a> {% endfor %}</p>
{% endfor %}
{% if has_previous %}
<div style="text-align:center;"><a href="/news/cat/{{ cat }}/?page={{ previous }}"><b>Nowsze Wiadomości</b></a></div>
{% endif %}
{% if has_next %}
<div style="text-align:center;"><a href="/news/cat/{{ cat }}/?page={{ next }}"><b>Starsze Wiadomości</b></a></div>
{% endif %}
{% else %}
Brak wiadomości
{% endif %}
{% endblock %}
Zastosowaliśmy widok rozszerzający generyczny widok listowania ze stronicowaniem (generic.list_detail) oraz szablon podobny do listy z głównej strony.
Teraz zajmiemy się wyświetleniem listy kategorii po prawej stronie strony. Wystarczy pobrać listę kategorii. Jednakże by była widoczna na każdej podstronie to każdy widok musiałby przekazywać zmienną z taką listą. By uniknąć takiego problemu zastosujemy własny TEMPLATE_CONTEXT_PROCESSORS, który będzie przekazywał do szablonów określone zmienne, niezależnie od tego co przekazują widoki. W katalogu projektu ("blog") stwórz plik o nazwie
globals.py, o kodzie:
from django.conf import settings
from blog.news.models import *
def blog(request):
return {'categories': NewsCategories.objects.all()}
Do
settings.py dodaj:
TEMPLATE_CONTEXT_PROCESSORS = ("django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"globals.blog",)
Pierwsze trzy wpisy to domyślnie włączone preprocesory django, które musimy podać jeżeli określamy własną listę preprocesorów. Ostatni wpis jest nasz, a składnia wpisu ma postać
plik.funkcja. Jak widzimy funkcja
blog zwraca słownik z kluczem "categories". W szablonie
index.html wycinamy prawe menu i dajemy:
<h3>Kategorie</h3>
<ul>
{% for c in categories %}
<li><img src="/site_media/{{ c.icon }}" alt="" /> <a href="/news/cat/{{ c.slug }}/">{{ c.name }}</a></li>
{% endfor %}
</ul>
Zamiast menu zobaczymy listę kategorii. Lista ta będzie widoczna na wszystkich generycznych widokach. U nas tylko
show_news nie jest generycznym widokiem. Wymaga on drobnej modyfikacji. W
views.py dodaj:
from django.template import RequestContext
A do "render_to_response" dodaj
, context_instance=RequestContext(request):
return render_to_response('show.html', {'news':news, 'form':form, 'comments':comments}, context_instance=RequestContext(request))
Gotowe:
W kolejnym artykule zajmiemy się polem "wykop" oraz stworzymy kanały RSS oraz mapę strony Sitemap.
Pobierz źródła
Po rozpakowaniu edytuj ścieżkę w
urls.py a następnie stwórz tabele i superadmina.
- Dodany: 14.07.2008 przez riklaunim