Django-rest-framework: Tambahkan/validasi tag dengan mudah.

Dibuat pada 29 Jun 2011  ·  24Komentar  ·  Sumber: encode/django-rest-framework

Django memiliki dekorator etag/condition/last_modified yang mengagumkan. Itu tidak berfungsi dengan tampilan berbasis kelas drf, karena Anda tidak dapat menghias 'dapatkan' dengannya. Karena get mengembalikan objek yang bukan merupakan respons http, tidak ada cara untuk menambahkan header etag ke respons.

Saya ingin melihat cara untuk melakukan ini dari dalam drf. Saya sedang memikirkan sesuatu yang sejalan dengan metode yang dapat ditimpa pada sumber daya, atau tampilan (atau mixin) yang dapat digunakan untuk menghasilkan etag.

Cara lain untuk melakukannya di Django adalah dengan menggunakan middleware, tetapi itu tidak dapat melakukan jalan pintas menjalankan badan tampilan sama sekali seperti yang dapat dilakukan oleh dekorator.

Enhancement

Komentar yang paling membantu

Sayangnya implementasi dan dokumentasi default pada fungsionalitas Etag di ekstensi drf salah dan sangat berbahaya. Itu mengubah Etag jika _request_ berubah, bukan jika _response_ berubah. Yang persis seperti yang Anda inginkan untuk caching sisi server, dan persis apa yang tidak Anda inginkan untuk Etag.

Semua 24 komentar

Sangat senang untuk umpan balik tentang ini, meskipun.

Oke, jadi, awalnya yang saya tulis adalah ini....

Keren, ya, saya sangat ingin melihat ini.

Gagasan coupla - Anda seharusnya dapat menggunakan View.add_header saja, bukan View._ETAG yang Anda miliki saat ini.
(Dan sepertinya .add_header mungkin harus dipindahkan ke kelas ResponseMixin.)

Kedua, saya ingin melihat dekorasi @condition , @etag dan @last_modified mungkin bisa menjadi tiruan langsung dari https://github.com/django/django/blob/master/django/views/decorators /http.py , hanya mengganti beberapa add_header dan ErrorResponses

Tapi melihat ke hal-hal sedikit lebih ...

Dan mungkin ini bukan cara yang tepat untuk pergi...

Anda benar-benar dapat mengembalikan HttpResponses dari tampilan kerangka kerja REST, mereka hanya tidak menerapkan semua hal negosiasi/serialisasi konten yang biasa diterapkan. Dekorator @last_modified , @etag dan @condition hanya mengembalikan HttpResponses kosong, jadi itu tidak terlalu menjadi masalah.

Jadi, apa yang saya pikirkan adalah, jika kita hanya menambahkan __setitem__ __getitem__ dan has_header ke kelas Response, maka saya pikir Django sudah ada @last_modified , Dekorator @etag dan @condition harus bekerja dengan baik pada tampilan kerangka REST _selama_ tampilan menggunakan gaya return Response(status, data) daripada gaya return data .

Obv akan sangat membantu jika kami mendokumentasikannya, tetapi mungkin lebih masuk akal daripada harus mereplikasi sesuatu yang sudah dilakukan Django.

Bagaimana menurut anda?

Mungkin ada satu masalah dengan menggunakan dekorator Django: Saya tidak yakin mereka akan bekerja dengan metode, hanya fungsi kosong. Dekorator yang saya tulis sangat didasarkan pada yang saya temukan melalui StackOverflow hanya untuk kasus ini.

Itu mungkin tidak terjadi, dalam hal ini solusi ini terdengar lebih unggul.

Karena itu, saya telah mengembalikan gaya data pengembalian, karena lebih sedikit boilerplate, dan saya biasanya hanya mengembalikan objek yang ingin saya ceritakan sebagai json. Kita mungkin bisa membuatnya bekerja dengan dua cara.

Pilihan lain mungkin mixin yang menambahkan ini ke kelas View.

Juga terpikir oleh saya bahwa dekorator Django mungkin tidak melakukan hal yang benar sehubungan dengan permintaan PUT, POST dan DELETE bersyarat. Baru saja mengirimkan tambalan ke sinatra untuk memperbaiki masalah itu.

Abaikan bit terakhir: jelas saya belum membaca kode dengan benar.

Sebenarnya ada benarnya: dekorator itu mungkin tidak bekerja ATM pada _methods_, karena mereka punya argumen 'self' tambahan. Saya akan menyelidikinya dan mungkin mengirimkan tiket ke Django, karena mereka juga harus bekerja dengan CBV...

Ah, oke - saya melihat @method_decorator sekarang... https://docs.djangoproject.com/en/dev/topics/class-based-views/#decorating -class -based-views
Jadi saya kira ini untuk ditutup kita perlu:

  1. Sedikit penyesuaian untuk Respon
  2. Beberapa dokumentasi ringan

Saya yakin saya melihat dokumen itu, tetapi saya tidak melihat dekorator itu!

Saya mencoba menggunakan dekorator Django dan hasil awalnya benar-benar aneh, dengan informasi yang seharusnya hanya dimiliki oleh tampilan yang muncul dalam fungsi etag yang tidak terkait.

Setelah beberapa jam mencoba membuatnya sedikit lebih baik, saya menemukan solusi yang menyelesaikan masalah saya dan tampaknya cukup umum. Apa pendapat Anda tentang ini:

https://bitbucket.org/vitormazzi/Django-rest-framework/changeset/6f8de4500c6f

Berharap untuk menghembuskan kehidupan baru ke dalam masalah ini sekarang karena kami berada di rilis 2.x.

Pikiran saya di sini sebagian besar disatukan dari mencoba menambahkan fungsionalitas dalam sebuah proyek dan dari membaca posting ini jadi pasti masih kasar.

Saya melihat dua area di mana DRF perlu mempertimbangkan ETag - penggunaan dalam tampilan dan cara mendapatkan representasi unik dari versi instance.

Tampilan

DAPATKAN
Permintaan GET hanya perlu melayani objek ETag di header yang sesuai. Perubahan satu baris ke RetrieveModelMixin dapat dengan mudah menambahkan ini:

def retrieve(self, request, *args, **kwargs):
    self.object = self.get_object()
    serializer = self.get_serializer(self.object)
    headers = {'ETag': self.object.etag}
    return Response(serializer.data, headers=headers)

PUT, PATCH, HAPUS
Pemeriksaan umum untuk memperbarui kata kerja HTTP dapat dilakukan di dispatch tampilan atau mungkin ditarik ke metode lain karena perlu memeriksa apakah ETag diaktifkan (lihat bagian opsi di bawah):

    header_etag = request.META.get('HTTP_IF_MATCH')
    if header_etag is None:
        return Response({'error': 'IF_MATCH header is required'}, status=400)

Kemudian pemeriksaan yang lebih mendetail setelah mengambil objek untuk melihat apakah permintaan tersebut menganggapnya sebagai objek yang benar:

    if self.object.etag != header_etag:
        return Response({'error': 'object has been updated since you last saw it'}, status=412)

Representasi Unik dari Versi Instans

Saya tidak berpikir generasi sebenarnya dari ETag objek seharusnya menjadi masalah DRF. Saya telah menguji menggunakan waktu Epoch bidang updated saya tetapi saya dapat dengan mudah melihat bahwa perlu lebih kompleks.

Saya mengusulkan agar DRF mencari obj.etag secara default tetapi dapat dikonfigurasi menggunakan aliran CBV normal, misalnya get_etag() dan etag_var = 'get_my_objects_etag' .

Kami juga perlu menerapkan ETag yang diambil dari objek sebagai string karena kami membandingkan dengan header dan mencoba menafsirkan jenisnya akan sangat menyakitkan.

Pilihan

  • Pengaturan global (seperti serializer, dll) untuk mengaktifkan atau menonaktifkan penggunaan ETag.
  • Dua pengaturan pada Tampilan:

    • use_etags (atau yang serupa) - sebuah boolean

    • etag_var - string nama fungsi yang kita dapat getattr pada objek yang bersangkutan

@ghickman - Saya ingin melihat perilaku untuk menentukan ETag dan LastModified terlihat mirip dengan kelas pluggable lainnya. Yaitu. Memiliki sesuatu seperti:

class MyView(views.APIView):
    cache_lookup_classes = []

Tanda tangan cache harus menangani ETag dan LastModified, dan ada dua hal berbeda yang ingin kami berikan:

  • Tentukan etag dan/atau modifikasi terakhir yang diberikan instance objek.
  • Menentukan etag dan/atau modifikasi terakhir secara preemtif berdasarkan permintaan yang masuk.

Akan ada BaseCacheLookup , dengan dua tanda tangan metode yang mungkin terlihat seperti:

.object_etag_and_last_modified(self, view, obj)
.preemptive_etag_and_last_modified(self, view, request, *view_kwargs, **view_kwargs)

Diberikan sebuah objek mengembalikan dua Tuple dari (etag, last modified) , yang salah satunya mungkin tidak ada.
Jika permintaan masuk berisi header If-Modified- Since atau If-None-Match yang cocok, maka respons 304 Not Modified akan dikembalikan. Jika respons yang masuk berisi If-Match atau If-Unmodified- Since yang cocok maka respons 412 Prekondisi Gagal akan dikembalikan.

Ini akan memungkinkan CacheLookupClass cocok dengan implementasi yang telah Anda jelaskan, tetapi juga varian lainnya.

Anda juga dapat menerapkan beberapa kelas pencarian cache, pada perincian modifikasi terakhir yang berbeda, misalnya,
sertakan GlobalLastModifiedLookup selain ObjectETagLookup . Itu akan memungkinkan tampilan untuk kembali secara preemptif sebelum membuat panggilan basis data apa pun jika tidak ada penulisan yang dilakukan sejak salinan yang di-cache. (Bahkan kebijakan yang sangat mendasar seperti itu bisa membuat perbedaan besar jika Anda menggunakan cache sisi server dengan Varnish)

Apakah sisi kelas pluggable ini terdengar masuk akal bagi Anda?

Saya belum memikirkan LastModified karena saya tidak menggunakannya dalam implementasi saya saat ini, tetapi masuk akal untuk memasukkannya mengingat tujuannya.

Kelas pluggable terdengar seperti ide bagus, terutama jika kami menyertakan implementasi LastModified dan ETag sebagai contoh dasar. Saya menyukai gagasan bahwa caching GET akan sangat mudah diaktifkan dengan sedikit perubahan pada suatu proyek.

Saya lebih suka membagi generasi etag dan last_modified menjadi dua metode (dari nama-nama itu) yang, seperti yang Anda sarankan, mengembalikan None saat tidak diterapkan. Backend CacheLookup kemudian dapat memilih untuk mengimplementasikan satu dan/atau yang lain. Kami selalu dapat menyediakan metode utilitas untuk kenyamanan ( cachable_obj_repr atau unique_obj_repr mungkin?) yang menggabungkan keduanya jika Anda merasa itu akan berguna.

tl; dr ya sisi kelas pluggable terdengar masuk akal dan harus memberikan fleksibilitas yang jauh lebih besar. Saya senang untuk mulai menulis tambalan untuk ini.

Halo semuanya. Jika Anda tertarik, saya telah menerapkan pendekatan berbeda untuk dukungan etag di perpustakaan ekstensi saya http://chibisov.github.io/drf-extensions/docs/

@chibisov Neato. Kami benar-benar harus menyelesaikan #1019 jadi kami punya tempat di dokumen untuk menautkan ke paket seperti ini.

Menutup ini karena #1019 telah ditutup dan paket @chibisov terdaftar.

Yang ini sengaja dibuat sebagai 3.3 karena saya ingin kami memberikan arahan formal tentang ini di beberapa titik. Tidak terlalu peduli jika kami memilih untuk menutup ini, tetapi itu sudah ada di peta jalan internal saya.

Sayangnya implementasi dan dokumentasi default pada fungsionalitas Etag di ekstensi drf salah dan sangat berbahaya. Itu mengubah Etag jika _request_ berubah, bukan jika _response_ berubah. Yang persis seperti yang Anda inginkan untuk caching sisi server, dan persis apa yang tidak Anda inginkan untuk Etag.

@mbox hal terbaik adalah membuka masalah tentang ini di drf-extensions atau jika menurut Anda ini adalah masalah "inti" DRF, buka di sini. Harap dicatat bahwa tes yang gagal akan menjadi awal yang baik bagi kami untuk melihat masalah ini.

@mbox @xordoquy
Saya baru saja mengirimkan PR ke drf-extensions (https://github.com/chibisov/drf-extensions/pull/171) yang memungkinkan kontrol konkurensi optimis untuk memanipulasi sumber daya melalui DRF API. Ini menggunakan hash semantik dari semua bidang objek dan saya menyertakan aplikasi uji untuk tujuan demonstrasi. Ini telah diuji terhadap DRF>=3.3.1 dan Django>=1.8 dengan Python 2.7, 3.4, 3.5.

Terima kasih

Sekedar catatan untuk pembaca masa depan - Saya telah membuat paket kecil untuk menggunakan dekorator bersyarat dari Django bersama dengan DRF. Jadi jika Anda tertarik:
https://github.com/jozo/Django-rest-framework-condition

Apakah halaman ini membantu?
0 / 5 - 0 peringkat