Herkese merhabalar. HTTP OPTIONS metodu RFC7231, Section 4.3.7. açıkca tanımlandığı üzere, istemcinin bir kaynakla ilişkili seçenekleri, gereksinimleri, veya sahip olduğu yetenekleri sorgulama sırasında kullanılır.
İstemci bu bilgileri kullanarak o kaynak hakkında bilgi sahibi olur ve gönderebileceği potansiyel istekleri belirtilen bilgilere göre düzenleyerek gönderir.
Bir HTTP OPTIONS isteği aşağıdaki gibi bir cevap dönebilir. Kaynakta hangi işlemlerin yapılabileceği, hangi data content type’ı kabul ettiği veya istemci tarafında kullanımı kolaylaştırma adına belirli validasyon işlemleri vs vs.
HTTP 200 OK Allow: GET, POST, HEAD, OPTIONS Content-Type: application/json { "name": "To Do List", "description": "List existing 'To Do' items, or create a new item.", "renders": [ "application/json", "text/html" ], "parses": [ "application/json", "application/x-www-form-urlencoded", "multipart/form-data" ], "actions": { "POST": { "note": { "type": "string", "required": false, "read_only": false, "label": "title", "max_length": 100 } } } }
Ortak olarak kabul edilmiş bir OPTIONS cevap şeması yoktur. İhtiyaçlarınız doğrultusunda hiç kullanmayabilir veya istemciye daha farklı veriler sağlayabilirsiniz.
Django Rest Framework bu ihtiyacı karşılayabilme adına Metadata ismi verilen özelleştirilebilir bir mekanizmaya sahiptir. Bugün bu yapıyı nasıl kullanabileceğimizi ve genel kullanım ihtiyaçlarına göre nasıl özelleştirebileceğimiz üzerine konuşacağız.
Kullanım.
Kaynaklar için sağladığımız metadatalar, diğer DRF mekanizmaları(authentication,permission) vs gibi pluggable (sökülüp-takılabilen) bir yapıdadır. Direkt projeniz genelinde veya spesifik bir view üzerinde kullanılabilmesi için tanımlayabilirsiniz.
Sırası ile global ve lokal kayıt.
REST_FRAMEWORK = { 'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata' }
class MyModelViewSet(viewsets.ModelViewSet): metadata_class = CustomMetadataClass ...
Özelleştirme.
DRF bize 2 adet varsayılan sınıf sağlamakta. BaseMetadata ve SimpleMetaData.
Özel bir meta verisi sağlamak istiyorsanız BaseMetadata sınıfını miras almalı ve determine_metadata(self, request, *args, **kwargs) metodunu override etmelisiniz.
Kısaca açıklayacak olur isek BaseMetadata pek fazla bilgi barındırmayan, SimpleMetadata ise daha detaylı bilgiler dönüleceği durumlarda kullanılmaya uygun.
BaseMetadata.
Örneğin bir kaynağın açıklaması ve kaynağın arkasında çalışan view ile ilgili bir takım bilgileri vereceğimiz bir senaryo kurgulayalım.
class BasicMeta(metadata.BaseMetadata): def determine_metadata(self, request, view): metadata = {} metadata["resource_name"] = view.get_view_name() metadata["resource_description"] = view.get_view_description() return metadata class Dummy(APIView): """ Lorem Ipsum is simply dummy text of the printing and typesetting industry. """ metadata_class = BasicMeta ...
determine_metadata metodu varsayılan olarak gelen request nesnesini ve istek yapılan viewı parametre olarak alır.
Daha sonrasında bilgileri tutacak bir sözlük oluşturuyor, belirli bilgileri sözlüğe bağlıyor ve geri dönüyoruz.
$ http options :8000/api/dummy/ HTTP/1.1 200 OK Allow: OPTIONS Content-Length: 125 Content-Type: application/json Date: Sun, 14 Feb 2021 11:42:59 GMT Referrer-Policy: same-origin Server: WSGIServer/0.2 CPython/3.8.5 Vary: Accept, Cookie X-Content-Type-Options: nosniff X-Frame-Options: DENY { "resource_description": "Lorem Ipsum is simply dummy text of the printing and typesetting industry.", "resource_name": "Dummy" }
SimpleMetadata.
SimpleMetadata kullancıya ilgili kaynak hakkında daha fazla bilgi sağlanması gereken durumlarda kullanılıyor. Kaynağın hangi isteklere cevap verebileceği, hangi istek tipinde ne gibi değerleri beklediği, bu işlemleri gerçekleştirir iken kullanılacak olan verinin validasyon bilgileri vs vs. Bir özelleştirme yapmaz iseniz, bu sınıf global olarak projenizdeki her kaynakta kullanılır durumdadır.
Bu sınıf, determine_metadata metodu yanında serializer fieldları üzerinde çalışırken de, verilen bilgileri özelleştirebileceğiniz get_field_info adlı bir metoda sahip.
Gerçek bir kullanım senaryosu üzerinden örnekleyecek olursak, beraber çalıştığınız takım arkadaşlarınız API’nizi kullanacak istemcileri yazarken, input alanlarını OPTIONS metodu üzerinde bulunan bir takım bilgilere göre doldurur. Bu doldurulan bilgiler arasında validasyon kuralları, dropdown seçenekler veya lokalize edilmiş label bilgileri gibi bilgiler bulunabilir.
Rest Framework ilişkisel alanları ModelSerializer yardımıyla serialize eder fakat Browsable API kullanımı dışında bir seçenek havuzu kullancıya dönmez.
Onun yerine aşağıda da görülebileceği üzere buranın bir field olduğu bilgisi dönülür.
$ http options :8000/api/dummy/ { "actions": { "POST": { "requested_by": { "label": "Requested by", "read_only": false, "required": false, "type": "field" } } },
Bunun sebebi performanstır. Potansiyel olarak 1 milyon kullanıcı havuzunu dönmek sistemi fazlası ile yorabilir. Fakat özelleştirilen bir metadata sınıfı ile ilk 5, 10 kullanıcıyı dönerek seçenek havuzu oluşturabilir daha sonrasında kullanıcıdan gelen isteğe göre ilgili ilişkisel alan üzerinde arama yapabilecek bir mekanizma da geliştirir isek performansı sunucu tarafında, kullanıcı deneyimini ise istemci tarafında arttırabiliriz. OPTIONS cevabı içerisinde seçenek havuzunu nasıl oluşturabileceğimize göz atarsak:
class RelatedFieldChoicesMetaData(metadata.SimpleMetadata): def get_field_info(self, field): field_info = super().get_field_info(field) if isinstance(field, (RelatedField, ManyRelatedField)): field_info["choices"] = [ {"value": value, "display_name": name} for value, name in field.get_choices(cutoff=5).items() ] # cutoff param will slice your related field queryset. return field_info class ExampleViewSet(viewsets.ModelViewSet): """ Lorem Ipsum is simply dummy text of the printing and typesetting industry. """ metadata_class = RelatedFieldChoicesMetaData ...
Tüm fieldlar üzerinde dolaşırken, üzerinde çalışılan field ilişkisel bir alan ise get_choices metodu ile queryseti alıyor daha sonra iteratif şekilde, hem value(tipi tanımlanan ilişkisel serializer alanına göre değişir.) hem de display value(queryset üzerinde bulunan nesnesin __str__ karşlığı) üzerinde dolaşarak bir liste geriye dönüp, field info’suna oluşturulan değeri ekliyoruz.
$ http options :8000/api/wishes/ HTTP/1.1 200 OK Allow: GET, POST, HEAD, OPTIONS Content-Length: 439 Content-Type: application/json Date: Sun, 14 Feb 2021 14:04:18 GMT Referrer-Policy: same-origin Server: WSGIServer/0.2 CPython/3.8.5 Vary: Accept X-Content-Type-Options: nosniff X-Frame-Options: DENY { "actions": { "POST": { "requested_by": { "choices": [ { "display_name": "egehan", "value": 1 }, { "display_name": "egehan2", "value": 2 } ], "label": "Requested by", "read_only": false, "required": false, "type": "field" } } }, ... continues
Basit değil mi?
Sonuç.
İstemcilerinize, kaynak hakkında metadata sağlamak, geliştirme aşamasının kolaylaşması, takım arkadaşlarınızın olan biteni anlamdırabilmesini adına üzerinde durulması gereken bir konu. Yapılabilecekler ise tabi ki bu kadar ile sınırlı değil. Fakat olduğunca karşılaşılabilecek durumlar üzerinde durmaya çalıştım. Daha fazlası için dökümantasyon linki burada. Görüşmek üzere!
İlk Yorumu Siz Yapın