
Sudah beberapa minggu sejak rilis resmi versi 3 Django. Saya bekerja dengan versi ini bahkan sebelum publikasi rilis resmi dan, sayangnya, saya perhatikan bahwa perkembangan Django melambat secara signifikan. Versi 1.3 berbeda dari 1.7 di kali, tetapi versi 3 berisi perubahan kosmetik untuk cabang 2 dan tidak lebih.
Proyek winePad saya dimulai dengan Django 1.3, dan sekarang telah mendefinisikan ulang sekitar 12% dari kode internal Django.
Melihat kode untuk versi baru, saya mengerti bahwa suntingan yang saya atau kolega saya buat saat bekerja dengan versi sebelumnya akan melangkah lebih jauh. Dan melihat peta jalan dan perubahan lamban dalam repositori resmi, Anda tidak perlu menunggu kesalahan diperbaiki di versi mendatang.
Inilah yang ingin saya bicarakan tentang mengatasi bug:
Dapatkan metode
Hanya sedikit orang menyadari bahwa ada kesalahan dalam standar mendapatkan metode Django dari awal. Metode get seharusnya mengembalikan satu objek kepada Anda, atau memperingatkan bahwa beberapa objek ditemukan, atau melaporkan bahwa tidak ada objek.
1 def get(self, *args, **kwargs): 2 clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs) 3 if self.query.can_filter() and not self.query.distinct_fields: 4 clone = clone.order_by() 5 limit = None 6 if not clone.query.select_for_update or connections[clone.db].features.supports_select_for_update_with_limit: 7 limit = MAX_GET_RESULTS 8 clone.query.set_limits(high=limit) 9 num = len(clone) 10 if num == 1: 11 return clone._result_cache[0] 12 if not num: 13 raise self.model.DoesNotExist() 14 raise self.model.MultipleObjectsReturned()
Baris 9 menerima data pada SEMUA catatan yang ditentukan dalam queryset dan menerjemahkannya ke dalam satu set objek. Ada peringatan tentang ini di dokumentasi.
Sebelum versi 3, tidak ada batasan jumlah objek yang diminta. Ini berarti menerima sepenuhnya semua data dan mengubahnya menjadi objek, sebelum memberikan peringatan bahwa ada banyak objek.
Akibatnya, Anda bisa mendapatkan beberapa juta objek dalam memori, hanya untuk mengetahui bahwa lebih dari 1 objek ditemukan. Sekarang ada garis 5,7,8. Dan sekarang Anda dengan bangga mendapatkan hanya MAX_GET_RESULTS = 21 objek sebelum Anda tahu bahwa ada lebih dari 1 objek.
Dengan "__init__" yang berat, penundaan akan signifikan. Cara mengobati:
Ganti MAX_GET_RESULTS di django.db.models.query.py
menimpa GET atau sebelum memanggil GET gunakan:
vars(queryset.query).update({'high_mark':2, 'low_mark':0}) queryset.query.set_limits(0,2)
__Init__ metode model Django
Pernyataan dalam kode metode bawaan __init__ tidak jelas
_setattr=setattr
ini mungkin untuk kode mempercepat palsu dengan mentransfer referensi fungsi ke kamus lokal, tetapi ini bukan tentang itu. Ada beberapa masalah:
1. Jika Anda melewati atribut tambahan = pasangan nilai dalam model __init__, Anda akan mendapatkan "mendapat argumen kata kunci yang tidak terduga".
Dalam hal ini, saya sarankan untuk tidak membebani metode __init__, tetapi untuk menambahkan atribut setelah inisialisasi:
obj = MyClass() vars(obj).update({'attr':val, 'attr2':val2 ...})
2. Django baru menambahkan kemampuan untuk mendefinisikan kembali deskriptor pada setiap bidang model (Field.descriptor_class). Tapi tidak satu deskriptor yang tahu apakah objek diinisialisasi atau tidak. Ini diperlukan, misalnya, jika deskriptor akan menggunakan data dari objek prefetch_related yang akan muncul hanya setelah inisialisasi objek utama.
Saya tidak ingin menggunakan sinyal akhir inisialisasi, karena mungkin ada banyak pelanggan.
Dalam hal ini, saya tidak menemukan sesuatu yang lebih pintar daripada mengesampingkan __init__ dan menambahkan atribut untuk mengakhiri inisialisasi.
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._end_init = True
Querysets
EmptyQuerySet / DateQuerySet yang telah dikodekan telah dihapus, itu sudah bagus. Namun, situasi dengan queryset sebagai manajer saya secara ideologis tidak suka.
Jika saya ingin mengganti kelas QuerySet yang dibuat oleh manajer, saya menambahkan atribut _queryset_class
class MyManager(models.Manager): _queryset_class = MyQuerySet
Perhatian, untuk versi yang lebih lama ini tidak berfungsi, misalnya, Anda dapat melakukan ini:
class MyManager(models.Manager): def get_query_set(self): response = super(MyManager, self).get_query_set() response.__class__ = MyQuerySet return response
panel admin inlineFormset
Ada beberapa masalah:
1. Tidak mungkin untuk menampilkan catatan inlineFormset standar yang tidak memiliki koneksi langsung dengan objek bentuk utama. Misalnya: formulir untuk mengedit harga produk. Di tengah-tengah terletak referensi statistik bentuk Tabularinline dari harga pembelian grosir untuk barang "serupa".
Itu dipecahkan dengan mengganti metode model inline get_formset dan membuat MyFormSet Anda sendiri yang diwarisi dari BaseInlineFormSet
def get_formset(self, request, obj=None, **kwargs): kwargs['formset'] = MyFormSet super().get_formset(request, obj, **kwargs) class MyFormSet(BaseInlineFormSet): pass
2. Jika Anda mengedit objek dengan inlineformset di panel admin, dan saat ini seseorang akan menghapus salah satu catatan objek di dalam inlineformset melalui mekanisme lain, Anda akan menerima kesalahan dan Anda tidak akan dapat menyimpan objek. Hanya melalui kopy paste di jendela browser baru.
Saya hanya menemukan satu solusi sejauh ini - jangan gunakan inlineformset.
Panel admin
"Fitur pembunuh" dari Django, adalah kaktus terbesar dari proyek:
1. Tindakan "Hapus objek" dalam model administrator terlihat secara default, tidak masalah apakah pengguna memiliki hak untuk menghapus atau tidak.
Ini diselesaikan dengan menonaktifkan aksi ini secara default:
admin.site.disable_action('delete_selected')
2. Membuat hak pengguna tambahan dari panel admin tidak akan mungkin sampai Anda mengaktifkan administrator model Izin:
from django.contrib.auth.models import Permission class PermissionsAdmin(admin.ModelAdmin): search_fields = ('name', 'codename','content_type__app_label', 'content_type__model') list_display = ('name', 'codename',) actions = None admin.site.register(Permission, PermissionsAdmin)
3. Sayangnya, hak untuk mengakses hanya objek-objek tertentu di Django tidak ada.
Ini dapat diatasi dengan menulis entri ke djangoAdminLog dengan bendera khusus.
Dan kemudian periksa benderanya:
user.logentry_set.filter(action_flag=ENABLED, .....).exists()
4. Jika Anda membuat tindakan administrator seperti yang dijelaskan dalam dokumentasi, ingatlah bahwa itu tidak dicatat secara otomatis di djangoAdminLog.
5. Kelemahan lain dari bagian dokumentasi ini adalah bahwa semua contoh hanya pada fungsi. Tetapi bagaimana dengan GCBV? Dalam proyek saya, semua tindakan administrator model ditransfer ke GCBV.
Repositori.Koneksi tindakan standar:
class MyAdmin(admin.ModelAdmin): actions = (MyActionBasedOnActionView.as_view(),)
ContentType - registri model Django
50% genius / 50% dullness.
Tidak ada model yang memiliki akses ke registri model default.
Dalam proyek kami, ini diselesaikan dengan menambahkan mixin ke semua kelas:
from django.contrib.contenttypes.models import ContentType class ExportMixin(object): @classmethod def ct(cls): if not hasattr(cls, '_ct'): cls._ct, create = ContentType.objects.get_or_create(**cls.get_app_model_dict()) if create: cls._ct.name = cls._ct.model._meta.verbose_name cls._ct.save() return cls._ct @classmethod def get_model_name(cls): if not hasattr(cls, '_model_name'): cls._model_name = cls.__name__.lower() return cls._model_name @classmethod def get_app_name(cls): if not hasattr(cls, '_app_name'): cls._app_name = cls._meta.app_label.lower() return cls._app_name @classmethod def get_app_model_dict(cls): if not hasattr(cls, '_format_kwargs'): cls._format_kwargs = {'app_label': cls.get_app_name(), 'model': cls.get_model_name()} return cls._format_kwargs
sekarang kita dapat memanggil obj.ct () jika perlu.
Usermodel
Kemampuan untuk mengganti model pengguna muncul di versi 1.5.
Tetapi untuk versi 3, mereka tidak memperbaiki model = Pengguna dalam standar UserCreationForm / UserChangeForm.
Itu dipecahkan:
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import get_user_model class MyUserCreationForm(UserCreationForm): class Meta: model= get_user_model()
Sistem terjemahan
Tag yang terlihat oleh pengguna diberi tag
{% trans %}
atau dalam kode melalui
gettext_lazy
Namun, tidak ada pengelolaan sumber daya ini di panel admin. Umumnya.
Ada solusi eksternal, semuanya bekerja entah bagaimana.
Sebagai contoh, Rosetta secara sistematis kehilangan teks, dan antarmuka bekerja buggy. Tidak ada verifikasi hak akses untuk terjemahan di mana pun. Makemessage / kompilemage sistematis diperlukan untuk bekerja ...
Di winePad, tag trans, blocktrans, dan gettext_lazy didefinisikan ulang dan kami mulai menerima teks dari cache. Jika tidak ada cache, maka permintaan cache dari basis get_or_create menyelamatkan kami dari makemessage.
Topik multibahasa pada umumnya rumit. Solusi bawaan Django hanya berfungsi untuk teks statis. Tetapi masih ada kebutuhan untuk menerjemahkan model-model ini. Saya mencoba dengan cara saya sendiri untuk menyelesaikan masalah menerjemahkan teks-teks dinamis dalam proyek
Django-TOF , di mana saya menggabungkan kemampuan terjemahan model dan Parler / Hvad. Mungkin seseorang akan tertarik mengintip.
Untuk saat ini, saya akan menghentikan narasinya, karena artikel perbaikan bug Django dapat dengan mudah berubah menjadi tulisan lama.
Tolong beritahu saya bagaimana Anda meningkatkan Django Anda. Jika ada kelanjutan, saya mensistematisasikan ide-ide yang muncul.
ps Beberapa kode ditulis dalam notasi lama, saya harap pengertian, waktu atau staf untuk refactoring tidak selalu ditemukan.