RkBlog

Hardware, programming and astronomy tutorials and reviews.

Lista wstecznie niezgodnych zmian wprowadzonych w Django 1.0

Lista wstecznie niezgodnych zmian wprowadzonych do Django 1.0 względem Django 0.96

Ze względu na młody wiek frameworka (sprzed wydania 1.0) twórcy postanowili nie utrzymywać pełnej wstecznej zgodności, przekładając nad to rozwój Django. W porównaniu z wersją 0.96 pojawiło się szereg wstecznie niezgodnych zmian. Osoby używające Django-SVN zapewne znają większą, poza np. tymi wprowadzonymi niedawno jak newforms-admin. Począwszy od wersji 1.0 wsteczna zgodność będzie zachowywana najlepiej jak to możliwe. Szczegółowa lista zmian na wiki Django.

Zmiana taga szablonów 'spaceless' by usuwał wszystkie spacje

Tag {{ spaceless }} usuwa wszystkie spacje pomiędzy tagami HTML, a nie jak wcześniej zachowując jedną spację z wielu. Taki kod
{% spaceless %}
<p>
    <a href="foo/">Foo</a>
</p>
{% endspaceless %}
W starszej wersji dałby wynik
<p> <a href="foo/">Foo</a> </p>
Po zmiana wynik będzie następujący:
<p><a href="foo/">Foo</a></p>

Zmiana `localflavor.usa` na `localflavor.us`

Usunięcie `LazyDate`

Pomocnik LazyDate został usunięty. Zastąpić można go obiektem datetime.now. Stary kod np:
class Article(models.Model):
    title = models.CharField(maxlength=100)
    published = models.DateField(default=LazyDate())
Można zmodyfikować do postaci:
from datetime import datetime
class Article(models.Model):
    title = models.CharField(maxlength=100)
    published = models.DateField(default=datetime.now)

Zmiany w obsłudze MySQL

W rewizji 5042 wprowadzono małą zmianę w obsłudze kolumn CHAR(n). Zmienia to nieco wynikowe tabele wygenerowane przez Django, lecz nie wymaga zmian w kodzie.

LOGIN_URL opcją ustawień

W r5072 LOGIN_URL z komponentu autoryzacji został przeniesiony do ustawień umożliwiając elastyczniejszą zmianę. Stary kod w postaci:
from django.contrib.auth import LOGIN_URL
Powinien zostać zmieniony by odpowiadać settings.LOGIN_URL.

Zmiana metody Test Client `login()`

django.test.Client.login() obecnie nie wykorzystuje specyficznych szablonów i formularzy logowania, lecz bezpośrednio próbuje tworzy odpowiednie ciasteczka i obiekty sesji umożliwiając wykorzystanie metody z dowolnym systemem logowania. Stary kod w postaci
c = Client()
c.login('/path/to/login','myuser','mypassword')
Należy zastąpić
c = Client()
c.login(username='myuser', password='mypassword')

Przeniesienie ogólnych (Generic) relacji

GenericRelation i GenericForeignKey zostały przeniesione do django.contrib.contenttypes, jako że bez tej aplikacji nie będą działały.

Newforms: clean_data zmienione na cleaned_data

Od rewizji 5237 sprawdzone (cleaned) dane z formularzy dostępne są przez cleaned_data.

Usunięcie dodatków dla przesyłania plików z Test Client

Ze względu na różne formy przesyłania plików usunięto pomocnika w Test Client odpowiadającego typowi FileField.

Zmiana FloatField na DecimalField

Zmieniono nazwę typu danych "FloatField" na "DecimalField", a także reprezentowanie jej w Pythonie jako typ Decimal, a nie Float (co wcześniej powodowało utratę dokładności ze względu na zaokrąglanie). W modelach należy zmienić nazwę pola. Wprowadzono także typ "FloatField" przechowujące dane jako Float. Uwaga: w przypadku baz SQLite należy wymusić wyświetlanie danych w typie Decimal, a nie Float. Zrób kopię bazy danych (skopiuj plik) i wykonaj operacje:
./manage.py dumpdata --format=xml nazwa_aplikacji > data-dump.xml
./manage.py reset nazwa_aplikacji
./manage.py loaddata data-dump.xml

Keszowanie Urlpatterns

W celu zwiększenia szybkości działania frameworka reguły mapowania URLi są obecnie keszowane. Po ich zmianie konieczny jest restart Django.

Obsługa Unikodu

W rewizji 5609 dodano kod z UnicodeBranch odpowiedzialny za obsługę unikodu w Django. W przypadku danych nie-ASCII będzie powodowało to problemy. Opis zapalnych miejsc opisano na wiki Django. W skrócie:

Zmiana parametrów __init__() klasy Feed

Metoda __init__ klasy Feed jako drugi argument może przyjąć obiekt HttpRequest, a nie URL feeda, co umożliwia wykorzystanie klasy bez komponentu Sites. Zmiana wpłynie jedynie na klasy dziedziczące klasę Feed, lub kod bezpośrednio wywołujący __init__().

Pliki PO muszą być kodowane w UTF-8

Ujednolicając kodowanie stosowane w Django, a także przez tłumaczy operujących na plikach gettexta wybrano kodowanie UTF-8 jako jedyne dla plików *po.

Dodano argument `interactive` do `run_tests`

Parametr `interactive` umożliwia dodanie testów do skryptów budujących, w których interaktywne pytania w czasie testów nie są możliwe do obsłużenia.

Zmiana pierwszego argumentu `run_tests`

Pierwszym argumentem "run_tests" jest obecnie lista etykiet testów, a nie lista aplikacji, których testy mają zostać wykonane. Zmiana ta wpłynie na osoby używające zmodyfikowanych test_runnerów.

Drobne zmiany argumentów i obsługi danych w newforms

Dodanie `FileField` i `ImageField` do newforms wymusiło drobne zmiany obsługi danych (łączenia). Zmiana ta nie będzie miała wpływu na większość użytkowników.

Zmiana poleceń management.py

By umożliwić dodawanie innych poleceń, a także by odchudzić duży plik dokonano kilku zmian w management.py. Zmianie uległo wywoływanie poleceń z np:
>>> from django.core import management
>>> management.flush(verbosity=0, interactive=False)
>>> management.load_data(['test_data'], verbosity=0)
Do postaci:
>>> from django.core import management
>>> management.call_command('flush', verbosity=0, interactive=False)
>>> management.call_command('loaddata', 'test_data', verbosity=0)

Zmiany w bibliotekach obsługujących bazy danych

Wewnątrz bibliotek ORMa obsługujących różne bazy danych nastąpiło szereg zmian, przez co większość wewnętrznych funkcji została przeniesiona lub ich nazwa została zmieniona. Funkcje te nie są udokumentowane i zmiany wymaga jedynie specyficzny kod, których ich używał bezpośrednio.

Generyczny widok archive_year nie wykonuje order_by() na przekazanym querysecie

Ujednolicając zachowanie generycznych widoków powiązanych z datą generyczny widok archive_year nie sortuje już wyników samodzielnie.

django-admin.py i manage.py wymuszenie opcji po poleceniach

Opcje takie jak --settings muszą być podane po poleceniach takich jak runserver. Np:
django-admin.py --settings=foo.bar runserver
Musi zostać zmodyfikowane do postaci:
django-admin.py runserver --settings=foo.bar

django.views.i18n.set_language wymaga żądania POST

Widok set_language() został zmieniony tak by przyjmować tylko żądania POST (a nie GET jak wcześniej), gdyż zmiana ustawień za pomocą żądania GET nie jest zalecana przez specyfikację HTTP.

Nagłówki django.http.HttpResponse niezależne od wielkości liter

Tag szablonów load obsługuje wywołania z kropką

Tag load obsługuje ścieżki zawierające kropki (w stylu Pythona):
{% load foo.bar %}
Będzie próbowało załadować "foo/bar.py"

Funkcja _() nie jest domyślnie dostępna

Od rewizji 6568 należy samemu zaimportować odpowiednią funkcję gettexta przeznaczoną do obsługi fraz do tłumaczenia, np:
from django.utils.translation import ugettext as _

Klasa `django.newforms.forms.SortedDictFromList` usunięta

Ze względu na obecność django.utils.datastructures.SortedDict usunięta została klasa django.newforms.forms.SortedDictFromList.

Automatyczne enkodowanie danych w szablonach

Dane (zmienne) przekazane do szablonów będą miały enkodowane tagi HTML. By uniknąć enkodowania można użyć filtra |save lub autoescape:
    {% autoescape off %}
    {{ zmienna }}
    {% endautoescape %}
    {{ zmienna|save }}

Usuniecie nieużywanych funkcji z obsługi sesji

Usunięto kilka nieużywanych funkcji z komponentu obsługi sesji.

Zmiana typu wyjątku ustawień

Zmianie uległy typy wyjątków generowanych w przypadku problemu z ustawieniami (brak pliku określonego przez DJANGO_SETTINGS_MODULE itd)

Generyczne widoki domyślnie mają allow_empty na True

Zmieniono domyślną wartość allow_empty na True dla archive_index() i object_list()

`MultipleObjectsReturned` zamiast `AssertionError`

Jeżeli QuerySet.get() zwróci więcej niż jeden rekord zostanie wyrzucony wyjątek `MultipleObjectsReturned`.

Zmiana zachowania APPEND_SLASH

Jeżeli APPEND_SLASH ustawione jest na True, a Django dostanie URL nie kończący się ukośnikiem / sprawdzi czy może dopasować taki odnośnik zanim przekieruje na odnośnik z zamykającym ukośnikiem.

Ujednolicenie konstruktorów ModelForm's i Form's

Przed:
# formularz dodania
obj = MyObj()
form = MyForm(obj)

# formularz edycji
obj = MyObj.objects.get(pk=1)
form = MyForm(obj)
Obecnie:
# formularz dodania
form = MyForm()

# formularz edycji
obj = MyObj.objects.get(pk=1)
form = MyForm(instance=obj)

Wyrzucanie wyjątku jeżeli extends nie jest pierwszym tagiem użytym w szablonie

Jeżeli tag "extends" jest używany to musi znaleźć się przed innymi tagami.

Zmiana es_AR na es-ar w LANGUAGES

Zmiana Field.get_internal_type()

Dekoratory dziedziczą atrybuty funkcji, na których są używane

Usunięcie ado_mssql

Sterownik do ado_mssql został usunięty ze względu na brak obsługi. Jako alternatywę można użyć http://code.google.com/p/django-mssql/ lub http://code.google.com/p/django-pyodbc/.

Queryset-refactor

W rewizji 7477 dodano kod z gałęzi Queryset-refactor, co zmienia nieco korzystanie z ORM, np. relacji OneToOne. Zmiany opisane są w dokumentacji relacji/modeli.

Ściślejsze przypisywanie do ForeignKey

Zwiększono restrykcje dotyczące obiektów przypisywanych do ForeignKey i OneToOne.

Usunięto OrderingField

Usunięto pole OrderingField, które pojawiło się w gałęzi MagicRemoval, lecz nigdy nie zostało wykorzystane.

Pola BooleanFields wymuszają "required"

Dla pól BooleanField opcja "required=True" oznacza iż pole musi zostać zaznaczone. Jeżeli zaznaczenie pola nie jest wymagane należy dodać required=False.

Usunięcie domyślnego sortowania User

Ze względu na duże wykorzystanie tabeli związanej z django.contrib.auth.models.User usunięto domyślne sortowania.

Zmiany przesyłania plików

Zmianie uległ sposób przesyłania plików. Przed zmianą request.FILES zawierał dane w słownikach, a obecnie dane dostępne są w odpowiednim obiekcie. Całość zmian opisana jest w dokumentacji przesyłania plików.

Narzędzia do tłumaczeń częścią `django-admin.py`

Funkcje pełnione przez make-messages.py zostały przeniesione do django-admin.py:
django-admin.py makemessages -l xx
django-admin.py compilemessages -l xx

Usunięty sterownik MySQL_old

Usunięto sterownik dla przestarzałej wersji `MySQLdb`

Zmiany w metodach save_add, save_change i render_change_form z ModelAdmin

Usunięto kilka wewnętrznych funcji, w których model był przekazywany bez celu.

Generyczne widoki używają newforms

Generyczne widoki dodawania/edycji używają newforms.

Panel Admina generowany przez komponent newforms-admin

Wraz z obsługą newforms w Panelu Admina wprowadzono liczne zmiany w funkcjonowaniu i konfiguracji Panelu Admina. Dla urls.py:
# STARE:
from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^admin/', include('django.contrib.admin.urls')),
)

# NOWE:
from django.conf.urls.defaults import *
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/(.*)', admin.site.root),
)

Przeniesienie z modelu ```class Admin``` do klas ```ModelAdmin```

Panel Admina dla danego modelu nie jest już konfigurowany przez podklasę Admin w modelu, lecz przez klasę umieszczoną w admin.py
# STARE:
# models.py
from django.db import models

class MyModel(models.Model):
    name = models.CharField(max_length=100)

    class Admin:
        pass

# NOWE:
# models.py
from django.db import models

class MyModel(models.Model):
    name = models.CharField(max_length=100)

# admin.py
from django.contrib import admin
from myproject.myapp.models import MyModel

admin.site.register(MyModel)
Wszystkie opcje za wyjątkiem tych wymienionych poniżej nadal obowiązują.
# STARE:
# models.py
from django.db import models

class MyModel(models.Model):
    name = models.CharField(max_length=100)

    class Admin:
        list_display = ('name',)

# NOWE:
# models.py
from django.db import models

class MyModel(models.Model):
    name = models.CharField(max_length=100)

# admin.py
from django.contrib import admin
from myproject.myapp.models import MyModel

class MyModelAdmin(admin.ModelAdmin):
    list_display = ('name',)

admin.site.register(MyModel, MyModelAdmin)

Zmiana opcji Admin.manager

Klasa ModelAdmin może mieć zdefiniowaną metodę queryset:
class BookAdmin(admin.ModelAdmin):
    def queryset(self, request):
        """
        Filtrowanie po obecnie zalogowanym użytkowniku
        """
        return self.model._default_manager.filter(user=request.user)

prepopulate_from definiowane w klasie Admin, a nie w modelu

W klasie ModelAdmin dodano opcję prepopulated_fields, która zastąpiła prepopulate_from z modeli.
# STARE:
class MyModel(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    slug = models.CharField(max_length=60, prepopulate_from=('first_name', 'last_name'))

    class Admin:
        pass

# NOWE:
class MyModel(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    slug = models.CharField(max_length=60)

from django.contrib import admin

class MyModelAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug': ('first_name', 'last_name')}

admin.site.register(MyModel, MyModelAdmin)

Przeniesienie dokumentacji PA do django.contrib.admindocs

Zmiana 'fields' na 'fieldsets', zmiana wartości 'classes'

"Fields" jest używane do ustawiania kolejności w formularzu. Obecnie akceptuje jedynie listę pól. "Fieldsets" służy także do ich grupowania. Jeżeli "classes" jest podane w specyfikacji pola, to zwracana wartość musi być zmieniona ze stringa na tuplę stringów:
# STARE:
class MyModelA(models.Model):
   class Admin:
     fields = ('field1','field2','field3','field4')

class MyModelB(models.Model):
   class Admin:
     fields = (
         ('group1', {'fields': ('field1','field2'), 'classes': 'collapse'}),
         ('group2', {'fields': ('field3','field4'), 'classes': 'collapse wide'}),
     )

# NOWE:
class MyModelAdmin(admin.ModelAdmin):
    fields = ('field1', 'field2', 'field3', 'field4')  # Renaming is optional 

class AnotherModelAdmin(admin.ModelAdmin):
     fieldsets = (
         ('group1', {'fields': ('field1','field2'), 'classes': ('collapse',)}),
         ('group2', {'fields': ('field3','field4'), 'classes': ('collapse', 'wide')}),
     )

Edycja zależnych modeli (inline editing)

# STARE:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    author = models.ForeignKey(Author, edit_inline=models.TABULAR)
    title = models.CharField(max_length=100)

# NOWE:
# edit_inline nie jest potrzebne

from django.contrib import admin

class BookInline(admin.TabularInline): 
    model = Book
    extra = 3

class AuthorAdmin(admin.ModelAdmin):
    inlines = [BookInline]

admin.site.register(Author, AuthorAdmin)
Więcej szczegółów znajdziemy w dokumentacji.

Zmiana opcji `js` na `media`

# STARE:
class MyModel(models.Model):
    # not relavent, but here for show
    field1 = models.CharField(max_length=100)
    
    class Admin:
        js = (
            "/static/my_code.js",
        )

# NOWE:
# in admin.py

class MyModelAdmin(admin.ModelAdmin):
    class Media:
        js = (
            "/static/my_code.js",
        )
Jeżeli URLe nie są bezwzględne to dostaną prefiks settings.ADMIN_MEDIA_PREFIX dla newforms-admin (a ogólnie stosowany będzie prefiks settings.MEDIA_URL).

Przeniesienie raw_id_admin z modelu

Składnia jest niezależna od modelu
# STARE:
class MyModel(models.Model): 
    field1 = models.ForeignKey(AnotherModel, raw_id_admin=True)
    
    class Admin:
        pass

# NOWE:
class MyModelAdmin(admin.ModelAdmin):
    model = MyModel
    raw_id_fields = ('field1',) 

```django.contrib.auth``` używa newforms

Przeniesienie radio_admin z modelu

# STARE:
class MyModel(models.Model): 
    field1 = models.ForeignKey(AnotherModel, radio_admin=models.VERTICAL)
    
    class Admin:
        pass

# NOWE:
class MyModelAdmin(admin.ModelAdmin):
    model = MyModel
    radio_fields = {'field1': admin.VERTICAL}

Przeniesienie filter_interface z modelu

# STARE:
class MyModel(models.Model):
    field1 = models.ManyToManyField(AnotherModel, filter_interface=models.VERTICAL)
    field2 = models.ManyToManyField(YetAnotherModel, filter_interface=models.HORIZONTAL)

# NOWE:
class MyModelAdmin(admin.ModelAdmin):
    filter_vertical = ('field1',)
    filter_horizontal = ('field2',)

Zmiana nazwy newforms na forms

Zmiana określania ścieżek URL

URLe dzielone są przez serwer na dwie części - `SCRIPT_NAME` i `PATH_INFO`. Od rewizji 8015 Django bierze pod uwagę `SCRIPT_NAME` umożliwiając łatwiejsze udostępnianie aplikacji pod różnymi odnośnikami w domenie np. www.strona.pl/django/serwis/*.

mod_python

Można pozostawić konfigurację bez zmian i wszystko będzie działać, lecz można też zastosować wprowadzone zmiany. Standardowa konfiguracja typu:
<Location "/mysite/">
   ...
</Location>
powinna wyglądać tak:
<Location "/mysite/">
   PythonOption django.root /mysite     # Nowa linia
   ...
</Location>
Opis tej opcji znajduje się w dokumentacji Django + mod_python. Jej zaletą jest możliwość usunięcia "/mysite" ze wszystkich odnośników stosowanych w kodzie aplikacji.

Apache + fastcgi

Wystarczy usunąć prefiks z URLi, nie trzeba zmieniać konfiguracji serwera.

lighttpd + fastcgi (and others)

Lighttpd nie przekazuje pełnych danych do ustalenia SCRIPT_NAME. By to rozwiązać należy w settings.py wymusić wartość tej zmiennej, np:
FORCE_SCRIPT_NAME="/"

Zmiany działania filtru __in=[] dla pól numerycznych

authors = Author.objects.filter(id__in=author_ids.split(','))
Powyższy przykład nie będzie działać jeżeli author_ids jest pusty.

Ograniczenia typów wartości dla DecimalField

Django wyrzuci wyjątek jeżeli do pola DecimalField przypisana zostanie wartość typu Float.

Zmiana systemu resetowania hasła

Zmianie uległ układ ogólnych widoków resetowania hasła. Obecnie do użytkownika wysyłany jest email z linkiem do formularza zmiany hasła. Token staje się nieaktywny po 3 dniach od wygenerowania lub po użyciu w tym czasie.

Usunięcie przestarzałych funkcjonalności dla Django 1.0

Ograniczenie dostępu do obiektów żądania

Tag `url` nie pomija wyjątku NoReverseMatch

Signal refactoring

Zaszły liczne zmiany w podsystemie sygnałów i slotów, które znacząco poprawiły ich wydajność. Obecną budowę tego komponentu znajdziemy w dokumentacji.

Nowe domyślne widżety w Panelu Admina

Zmiany w komponencie składowania plików

`save_add` i `save_change` usunięte z `ModelAdmin`

`save_add` i `save_change` zostały usunięte, a `response_add` i `response_change` zostały dodane.

Usunięcie przestarzałych funkcjonalności dla Django 1.0

Wewnętrzne zmiany w sterownikach baz danych

Zmianie uległa wewnętrzna konstrukcja sterowników do baz danych. Zmiana ta nie wpływa na działanie standardowych metod ORMa.

Added parameter to session backend save() method

Własne sterowniki (backend) sesji muszą zawierać parametr "must_create" w metodach "save()". Domyślnie jego wartość ustawiona jest na False. W przypadku True nowy obiekt sesji musi zostać stworzony lub wyrzucony zostanie wyjątek.

Czyszczenie danych sesji przy wylogowaniu

Zmiana nazw polskich pól z localflavour

Zmianie uległy nazw pól na lokalnych danych:

Usunięcie walidacji z modeli i pól modeli

Poprawiono MultiValueDict.iteritems by zwracała elementy a nie listę

Poprawki filtrów taga "timesince"

Usunięcie polecenia adminindex z django-admin

Zmiana django.contrib.comments

Nieudokumentowany stary system komentarzy został zastąpiony zupełnie nową aplikacją.

Usunięcie oldforms, validatorów i powiązanego kodu

!ModelForms made more like Forms

Nie można już uzyskać dostępu do pól ModelForm jako atrybuty. Można natomiast poprzez słownik.

`create()` i `get_or_create()` nie zaktualizują istniejącego obiektu

reverse() i tag szablonów url nie przyjmują dodatkowych argumentów

RkBlog

Django, 6 September 2008,

Comment article