Django Rest Framework - Reverse relations - django

how do you include related fields in the api?
class Foo(models.Model):
name = models.CharField(...)
class Bar(models.Model):
foo = models.ForeignKey(Foo)
description = models.CharField()
Each Foo has a couple of Bar's related to him, like images or what ever.
How do I get these Bar's displaying in the Foo's resource?
with tastypie its quit simple, im not sure with Django Rest Framework..

I got it working! Shweeet!
Ok this is what I did:
Created serializers, Views and URLS for the Bar object as described in the Quickstart docs of Django REST Framework.
Then in the Foo Serializer I did this:
class FooSerializer(serializers.HyperlinkedModelSerializer):
# note the name bar should be the same than the model Bar
bar = serializers.ManyHyperlinkedRelatedField(
source='bar_set', # this is the model class name (and add set, this is how you call the reverse relation of bar)
view_name='bar-detail' # the name of the URL, required
)
class Meta:
model = Listing
Actualy its real simple, the docs just dont show it well I would say..

These days you can achieve this by just simply adding the reverse relationship to the fields tuple.
In your case:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = (
'name',
'bar_set',
)
Now the "bar"-set will be included in your Foo response.

I couldn't get the above working because I have a model called FooSomething.
I found the following worked for me.
# models.py
class FooSomething(models.Model):
name = models.CharField(...)
class Bar(models.Model):
foo = models.ForeignKey(FooSomething, related_name='foosomethings')
description = models.CharField()
# serializer.py
class FooSomethingSerializer(serializers.ModelSerializer):
foosomethings = serializers.StringRelatedField(many=True)
class Meta:
model = FooSomething
fields = (
'name',
'foosomethings',
)

Related

Update three-level nested django model using serializer

I am trying to update one of my models (which is a nested model - three level actually as you can see below) and I am getting the following error:
AssertionError: The .update() method does not support writable nestedfields by default. Write an explicit .update() method for serializer SystemSettingsSerializer, or set read_only=True on nested serializer fields.
All day I have been reading about nested models and nested serializers, trying to add update and create methods setting fields as read_only=True but no matter what I did, it just didn't work :( :(
These are my models:
class SystemSettings(models.Model):
# ... some fields
class Components(models.Model):
settings = models.ForeignKey(SystemSettings, related_name="Components")
class SysComponent(models.Model):
class Meta:
abstarct = True
index = models.PositiveIntegerField(primery_key=True)
is_active = models.BooleanField(default=False)
component = NotImplemented
class Foo(SysComponent):
component = models.ForeignKey(Components, related_name="Foo")
class Bar(SysComponent):
component = models.ForeignKey(Components, related_name="Bar")
task_id = models.PositiveIntegerField(default=0)
and serializers:
class SystemSettingsSerializer(ModelSerializer):
Components = ComponentsSerializer(many=True)
class Meta:
model = SystemSettings
fields = [# some fields,
Components]
class ComponentsSerializer(ModelSerializer):
Foo = FooSerializer(many=True)
Bar = BarSerializer(many=True)
class Meta:
model = Components
fields = ['Foo',
'Bar']
class FooSerializer(ModelSerializer):
class Meta:
model = Foo
class BarSerializer(ModelSerializer):
class Meta:
model = Bar
My logic is the following:
I am fetching the SystemSettings via GET and display it in a form.
The user changes it as much as he want and by clicking submit I send it back via PUT.
As I said I am getting the error above after clicking submit.
Any help would be appreciated.
EDIT: I am using django 1.7.8 by the way

Serialize relation both ways with Django rest_framework

I wonder how to serialize the mutual relation between objects both ways with "djangorestframework". Currently, the relation only shows one way with this:
class MyPolys(models.Model):
name = models.CharField(max_length=20)
text = models.TextField()
poly = models.PolygonField()
class MyPages2(models.Model):
name = models.CharField(max_length=20)
body = models.TextField()
mypolys = models.ManyToManyField(MyPolys)
# ...
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
class MyPages2Serializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPages2
# ...
class MyPolyViewSet(viewsets.ReadOnlyModelViewSet):
queryset = testmodels.MyPolys.objects.all()
serializer_class = srlz.MyPolysSerializer
class MyPages2ViewSet(viewsets.ReadOnlyModelViewSet):
queryset = testmodels.MyPages2.objects.all()
serializer_class = srlz.MyPages2Serializer
The many-to-many relation shows up just fine in the api for MyPages2 but nor for MyPolys. How do I make rest_framework aware that the relation goes both ways and needs to be serialized both ways?
The question also applies to one-to-many relations btw.
So far, from reading the documentation and googling, I can't figure out how do that.
Just do it like this:
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
fields =('id','name','text','poly')
class MyPages2Serializer(serializers.HyperlinkedModelSerializer):
mypolys = MyPolysSerializer(many=True,read_only=True)
class Meta:
model = testmodels.MyPages2
fields =('id','name','body','mypolys')
I figured it out! It appears that by adding a mypolys = models.ManyToManyField(MyPolys) to the MyPages2 class, Django has indeed automatically added a similar field called mypages2_set to the MyPolys class, so the serializer looks like this:
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
fields = ('name', 'text', 'id', 'url', 'mypages2_set')
I found out by inspecting an instance of the class in the shell using ./manage.py shell:
pol = testmodels.MyPolys.objects.get(pk=1)
pol. # hit the tab key after '.'
Hitting the tab key after the '.' reveals additional fields and methods including mypages2_set.

Django-REST Serializing RelatedField

I'm trying to serialize a model with RelatedFields as in the docs (http://www.django-rest-framework.org/api-guide/relations) but I keep getting an AttributeError.
The Error:
AttributeError at /testapi/foo/
'Foo' object has no attribute 'bar1'
The Models:
class Foo(models.Model):
foo_id = models.AutoField(primary_key=True)
name = models.TextField()
zip_code = models.TextField()
class Bar(models.Model):
user = models.OneToOneField(User)
arbitrary_field1 = models.ForeignKey(Foo, related_name='bar1')
arbitrary_field2 = models.ForeignKey(Foo, related_name='bar2')
The Serializer:
class FooSerializer(serializers.ModelSerializer):
bar1 = serializers.RelatedField()
bar2 = serializers.RelatedField()
class Meta:
model = Foo
fields = (
'foo_id',
'name',
'zip_code',
'bar1',
'bar2',
)
I may be completely wrong with this answer as I don't have a test environment set up right now but I'll do my best.
Ok, tried it in a test environment and the solution below is working for me.
I do not believe the RelatedField is needed in this case, since you are specifying the related_name for both the arbitrary fields you'd just need to reference the related name in the fields tuple instead of specifying them with RelateField in the serializer.
So you'd probably be fine with just this:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = (
'foo_id',
'name',
'zip_code',
'bar1',
'bar2',
)
This will return serialized data similar to this:
{
....
"bar1": [
1,
2,
3,
4
],
"bar2": [
1,
2,
3
]
....
}
Just a side note, but if you don't want just the pk's of the relationship you can try setting the depth variable in the Meta class of the Foo serializer to 1 or whatever number you'd like, example from the docs http://www.django-rest-framework.org/api-guide/serializers#specifying-nested-serialization.
Or if you want a more custom representation of that relationship you can just create a serializer for the Bar model with whatever fields you want to be included or excluded and use that serializer in the Foo serializer, just like this example in the docs http://www.django-rest-framework.org/api-guide/relations#example.
Edit:
Here is the DRF documentation on reverse relationships http://www.django-rest-framework.org/api-guide/relations#reverse-relations

django inlines with abstract model give error

I have the following in Django models.py (models are stripped down to only necessary fields)
class Product(BaseProduct):
price = models.IntegerField()
productfoto = models.ManyToManyField("ProductFoto", related_name="%(app_label)s_%(class)s_related")
class Meta:
abstract = True
ordering = ['name']
# Inherits Product class
class ConsumerProduct(Product):
categorie= models.ForeignKey(Categorie)
class Meta:
verbose_name_plural = "ConsumerProducten"
class ProductFoto(models.Model):
myimage= FileBrowseField("Image", max_length=200, directory='producten')
and this in admins.py:
class ProductFotoInline(admin.TabularInline):
extra = 1
model = Product.productfoto.through
class ConsumerProductAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("name",)}
inlines= [ProductFotoInline]
admin.site.register(ConsumerProduct, ConsumerProductAdmin)
Please take note of following:
Product class is abstract
ConsumerProduct inherits Product
I would say this should work, however I'm getting the following ImproperlyConfigured error when trying to add a new ConsumerProduct:
'model' is a required attribute of 'ConsumerProductAdmin.inlines[0]'.
Any help is appreciated
I've decided to make my Product class non-abstract. By removing the registration of this class with the admin (removing the line admin.site.register(Product))
You will not see it in the backend, however in your database, it will be a bit more messy, because you will have both a Product and a ConsumerProduct table

Django filters - Using an AllValuesFilter (with a LinkWidget) on a ManyToManyField

This is my first Stack Overflow question, so please let me know if I do anything wrong.
I wish to create an AllValues filter on a ManyToMany field using the wonderful django-filters application. Basically, I want to create a filter that looks like it does in the Admin, so I also want to use the LinkWidget too.
Unfortunately, I get an error (Invalid field name: 'operator') if I try this the standard way:
# Models
class Organisation(models.Model):
name = models.CharField()
...
class Sign(models.Model):
name = models.CharField()
operator = models.ManyToManyField('Organisation', blank=True)
...
# Filter
class SignFilter(LinkOrderFilterSet):
operator = django_filters.AllValuesFilter(widget=django_filters.widgets.LinkWidget)
class Meta:
model = Sign
fields = ['operator']
I got around this by creating my own filter with the many to many relationship hard coded:
# Models
class Organisation(models.Model):
name = models.CharField()
...
class Sign(models.Model):
name = models.CharField()
operator = models.ManyToManyField('Organisation', blank=True)
...
# Filter
class MyFilter(django_filters.ChoiceFilter):
#property
def field(self):
cd = {}
for row in self.model.objects.all():
orgs = row.operator.select_related().values()
for org in orgs:
cd[org['id']] = org['name']
choices = zip(cd.keys(), cd.values())
list.sort(choices, key=lambda x:(x[1], x[0]))
self.extra['choices'] = choices
return super(AllValuesFilter, self).field
class SignFilter(LinkOrderFilterSet):
operator = MyFilter(widget=django_filters.widgets.LinkWidget)
I am new to Python and Django. Can someone think of a more generic/elegant way of doing this?
Why did you subclass LinkOrderFilterSet?
Maybe the connect way to use it is this:
import django_filters
class SignFilter(django_filters.FilterSet):
operator = django_filters.AllValuesFilter(widget=django_filters.widgets.LinkWidget)
class Meta:
model = Sign
fields = ['operator']
You can use this
class CircleFilter(django_filters.FilterSet):
l = []
for c in Organisation.objects.all():
l.append((c.id, c.name))
operator = django_filters.ChoiceFilter(
choices=set(l))
class Meta:
model = Sign
fields = ['operator']