Package: django-rest-models
Allow to query adjangoRestAPI with same interface as the django ORM.(the targeted API must use django-rest-framework + dynamic-rest libraries)In fact, it works like any other database engine. You add the rest_models engine in an alternate database and the rest_models databe router.
Then add APIMeta class to the models querying the API, voilà !Stable branchDevelopment statusInstallationInstall using pip:pip installdjango-rest-modelsAlternatively, you can download or clone this repo and install with :pip install-e..RequirementsThis database wrapper work withpython 3.6, 3.7django 2.0, 2.1, 2.2, 3.0, 3.1, 3.2On the api, this is tested againstdjango-rest-framework 3.11, 3.12, 3.13dynamic-rest 2.1Examplessettings.py:DATABASES={'default':{...},'api':{'ENGINE':'rest_models.backend','NAME':'https://requestb.in/','USER':'userapi','PASSWORD':'passwordapi','AUTH':'rest_models.backend.auth.BasicAuth',},}DATABASE_ROUTERS=['rest_models.router.RestModelRouter',]models.py:classMyModel(models.Model):field=models.IntegerField()...classMeta:# basic django meta Stuffverbose_name='my model'# the only customisation that make this model specialclassAPIMeta:passclassMyOtherModel(models.Model):other_field=models.IntegerField()first_model=models.ForeignKey(MyModel,db_column='mymodel')...classMeta:# basic django meta Stuffverbose_name='my other model'# the only customisation that make this model specialclassAPIMeta:passTargeted API requirementsTo allow this database adapter to work like a relational one, the targeted API must respect some requirements :dynamic-rest installed and all serializers/views must respectively inherit from Dynamic* (DynamicModelSerializer, etc…)Each API serializer must :Provide the id fieldProvide the related field (ManyToMany and ForeignKey on Models) as DynamicRelationFieldProvide the reverse related field. We must, for each ForeignKey and ManyToMany, add a field on the related model’s
serializer.classMenuSerializer(DynamicModelSerializer):pizzas=DynamicRelationField('PizzaSerializer',many=True)# Menu.pizza = ManyToManyclassMeta:model=Menuname='menu'fields=('id','code','name','pizzas')deferred_fields=('pizza_set',)classPizzaSerializer(DynamicModelSerializer):toppings=DynamicRelationField(ToppingSerializer,many=True)menu=DynamicRelationField(MenuSerializer)# Add this because Menu.pizza = ManyToManyclassMeta:model=Pizzaname='pizza'fields=('id','name','price','from_date','to_date','toppings','menu')django-rest-models provide a way to check the consistency of the api with the local models via the django check framework.
At each startup, it will query the api with OPTIONS to check if the local models match the remote serializers.CaveatsSince this is not a real relational database, all feature cannot be implemented. Some limitations are inherited by
dynamic-rest filtering system too.Aggregations : is not implemented on the api endpoint, maybe in future releasesComplex filtering using OR : all filter passed to dynamic-rest is ANDed together, so no OR is possibleNegated AND in filtering: a negated AND give a OR, so previous limitation applyNegated OR in filtering: since the compitation of nested filter is complexe and error prone, we disable all OR. in
fact, only some nested of AND is accepted. only the final value of the Q() object can be negatedfor short, youCANNOT:Pizza.objects.aggregate()Pizza.objects.annotate()Pizza.objects.filter(Q(..)|Q(..))Pizza.objects.exclude(Q(..)&Q(..))Pizza.objects.exclude(Q(..)|Q(..))butyoucan:Pizza.objects.createPizza.objects.bulk_createPizza.objects.updatePizza.objects.bulk_updatePizza.objects.select_relatedPizza.objects.prefetch_relatedPizza.objects.valuesPizza.objects.values_listPizza.objects.deletePizza.objects.count()Pizza.objects.filter(...,...,...)Pizza.objects.filter(...).filter(...).exclude(...)Pizza.objects.exclude(...,...).exclude(...)Pizza.objects.filter(Q(..)&Q(..))Pizza.objects.none()pizza.toppings.add(...)pizza.toppings.remove(...)pizza.toppings.set(...)pizza.toppings.clear(...)Noteprefetch_related work as expected, but the performance is readly bad. As a matter of fact, aPizza.objects.prefetch_related('toppings')will query the toppings for all pizzas as expected, but the query to recover the pizza will contains the linked pizza in the response.
If the database contains a great number of pizzas for the given toppings, the response will contains them all, even if it’s
useless at first glance, the linked pizza for each topping is mandotary to django to glue topping <=> pizza relationships.So, be careful when using prefetch_related.Specific behaviourSome specific behaviour has been implemented to use the extra feature of a Rest API :When inserting, the resulting model is returned by the API. the inserted model is updated with the resulting values.
This imply 2 things:If you provided default values for fields in the api, these data will be populated into your created instance if it was ommited.If the serializer have some computed data, its data will always be used as a replacement of the one you gave to your
models. (cf example: Pizza.cost which is the sum of the cost of the toppling. after each save, its value will be updated)SupportThis database api support :select_relatedorder_byonlydeferfilterexcludedeleteupdatecreatebulk create (with retrive of pk)ManyToManyFieldForeignKey*NoteForeignKey must have db_column fixed to the name of the reflected field in the api. or all update/create won’t use
the value if this fieldNoteSupport for ForeignKey is only available with models on the same database (api<->api) or (default<->default).
It’s not possible to add a ForeignKey/ManyToMany field on a local model related to a remote model (with ApiMeta)DocumentationThe full documentation is athttp://django-rest-models.readthedocs.org/en/latest/.RequirementsPython 2.7, 3.5Django >= 1.8Contributions and pull requests are welcome.Bugs and requestsIf you found a bug or if you have a request for additional feature, please use the issue tracker on GitHub.https://github.com/Yupeek/django-rest-models/issuesknown limitationsJSONField from postgresql and mysql is supported by django-rest-models, but not by the current dynamic-rest (1.8.1)
so you can doMyModel.objects.filter(myjson__mydata__contains=’aaa’)but it will work if drest support itsame for DateField’s year,month,day lookup.LicenseYou can use this under GPLv3.AuthorOriginal author:Darius BERNARD.
Contributor:PaulWay.ThanksThanks to django for this amazing framework.