So I have follwoing models:
class A(models.Model):
name = models.CharField()
age = models.SmallIntergerField()
class B(models.Model):
a = models.OneToOneField(A)
salary = model.IntergerField()
Now I want to create one rest end point for there two as they are one to one. So I want following as get
{
url: 'http://localhost/customs/1/',
name: 'abc',
age: 24,
salary: 10000
}
Similary, I want to create records and update as well. Please let me know how can I achieve this in django rest framework 3.
I just encountered the same problem, it would indeed be useful to make the response structure less tied to the underlying model structure. Here's my take :
Reading is easy
Serializer fields have a source parameter, which can take dotted names to traverse attributes.
class ABSerializer(serializers.ModelSerializer):
class Meta:
model = A
fields = ['name', 'age', 'salary']
salary = serializer.IntegerField(source='b.salary') # this is your related_name
Writing is ... not officially supported
Validated data will show a nested structure, and the standard create and update methods will choke trying to assign a data dict to a OneToOneField.
The good news is that you can work around it by overriding create and update methods. Here's an example with update :
class ABSerializer(serializers.ModelSerializer):
class Meta:
model = A
fields = ['name', 'age', 'salary']
related_fields = ['b']
salary = serializer.IntegerField(source='b.salary') # this is your related_name
def update(self, instance, validated_data):
# Handle related objects
for related_obj_name in self.Meta.related_fields:
# Validated data will show the nested structure
data = validated_data.pop(related_obj_name)
related_instance = getattr(instance, related_obj_name)
# Same as default update implementation
for attr_name, value in data.items():
setattr(related_instance, attr_name, value)
related_instance.save()
return super(ABSerializer,self).update(instance, validated_data)
Of course, this example is very simplistic, doesn't do any exception handling, and won't work with more deeply nested objects... but you get the idea.
Another option
You could also create a read-write flavor of SerializerMethodField, which would consider both a getter and a setter, however that would probably end up being far more verbose in the end.
Hope that helps !
I know this is an old post but I came across this and after some research and reading through the Django Rest Framework documentation
So a quick search I found that you could use the related_name parameter for reverse relationships as stated here:
reverse relationships are not automatically included by the
ModelSerializer and HyperlinkedModelSerializer classes. To include
a reverse relationship, you must explicitly add it to the fields list.
For example:
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ['tracks', ...]
You'll normally want to ensure that you've set an appropriate
related_name argument on the relationship, that you can use as the
field name.
For example:
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks',
on_delete=models.CASCADE)
...
If you have not set a related name for the reverse relationship,
you'll need to use the automatically generated related name in the
fields argument.
For example:
class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ['track_set', ...]
Also, see the Django documentation on reverse
relationships
for more details.
Related
I have 2 models connected via M2M relation
class Paper(models.Model):
title = models.CharField(max_length=70)
authors = models.ManyToManyField(B, related_name='papers')
class Author():
name = models.CharField(max_length=70)
Is there a way to include authors as all related authors' IDs (and maybe name somehow)?
Is there a way to include papers IDs as reverse relation (and maybe title as well)?
Author.objects.all().annotate(related_papers=F('papers'))
this only adds id of one paper, first one it finds I think.
Furthermore, changing related_papers to papers gives an error:
ValueError: The annotation ‘papers’ conflicts with a field on the
model.
From what I understand in your comments, you're using DRF. I will give you 2 answers.
1) If you're talking about model serializer, you can use PrimaryKeyRelatedField :
class AuthorSerializer(serializers.ModelSerializer):
papers=serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Author
fields = ['name', 'papers']
class PaperSerializer(serializers.ModelSerializer):
class Meta:
model = Paper
fields = '__all__'
This will return the IDs for the other side of the relationship whether you're on Paper or Author side. That will return the primary keys, not a representation of the object itself.
2) Now you're also talking about performance (e.g. database hit at each iteration).
Django (not DRF-specific) has a queryset method to handle preloading related objects. It's called prefetch_related.
For example, if you know you're going to need the relation object attributes and want to avoid re-querying the database, do as follow:
Author.objects.all().prefetch_related('papers')
# papers will be already loaded, thus won't need another database hit if you iterate over them.
Actually, it has already been implemented for you. You should include a Many-to-Many relationship to author in your Paper model like this:
class Paper(models.Model):
title = models.CharField(max_length=70)
authors = models.ManyToManyField(Author, related_name='papers')
That gives you the opportunity to add Author objects to a related set using
p.authors.add(u), assuming that p is the object of Paper model, and a is an object of Author model.
You can access all related authors of a Paper instance using p.authors.all().
You can access all related papers of an Author instance using u.papers.all().
This will return an instance of QuerySet that you can operate on.
See this documentation page to learn more.
I am trying to write a generic method that can take any Django Model and returns it in a dictionary form.
So for example, if my models are defined thus (very generic):
class A(models.Model):
somefieldA = models.TextField()
m2mfield = models.ManyToManyField(B, through='AandB')
def __unicode__(self):
return self.somefieldA
class B(models.Model):
somefieldB = models.TextField()
def __unicode__(self):
return self.somefieldB
class AandB(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
field1 = models.DecimalField()
field2 = models.TextField()
field3 = models.DateField()
Now, assume we have an instance of the object A a_obj.
I can get all the related B objects using:
# This loop is there because I am working with other fields as well.
def instance_to_dict(instance):
for field in instance._meta.get_fields():
if field.many_to_many:
m2m_mgr = getattr(instance, field.name)
for idx, assoc_obj in enumerate(m2m_mgr.all()):
assoc_obj_str = str(assoc_obj)
# How to obtain the related through field values?
# m2m_mgr.through.objects.get() would need prior knowlegde
# of field name so get(a=instance, b=assoc_obj) is not possible
# m2m_mgr.through.objects.all() fetches all the objects
# in the Many to Many manager.
And then call instance_to_dict(a_obj). This method could be called by passing other models' instances.
Ideally, I would like to create a dict of the obj and related "through" fields for any object. Is this possible to do?
In addition to the explicitly defined ManyToMany manager, there is also an implicit reverse relationship for the ForeignKey from AandB to A. So you can do something like this:
for field in instance._meta.get_fields(include_hidden=True):
if field.one_to_many: # reverse ForeignKey
m2m_through_mgr = getattr(instance, field.get_accessor_name()) # e.g. aandb_set
m2m_through_mgr.all() # all related instances from the through table
Another approach is to go through the through table fields looking at field.related_model to see which one points back to your original table.
This all gets quite messy, but there should be enough meta information to do what you want. One obstacle is that the API isn't fully documented. Specifically, relation fields are represented by instances of the ManyToOneRel class, which as of Django 2.1 remains undocumented for reasons hinted at in the source code. Hence my use of the undocumented get_accessor_name() method.
I am trying to make a serializer with a nested "many to many" relationship. The goal is to get a serialized JSON object contain an array of serialized related objects. The models look like this (names changed, structure preserved)
from django.contrib.auth.models import User
PizzaTopping(models.Model):
name = models.CharField(max_length=255)
inventor = models.ForeignKey(User)
Pizza(models.Model):
name = models.CharField(max_length=255)
toppings = models.ManyToManyField(PizzaTopping)
The incoming JSON looks like this
{
"name": "My Pizza",
"toppings": [
{"name": "cheese", "inventor": "bob"},
{"name": "tomatoes", "inventor": "alice"}
]
}
My current serializer code looks like this
class ToppingRelatedField(RelatedField):
def get_queryset(self):
return Topping.objects.all()
def to_representation(self, instance):
return {'name': instance.name, 'inventor': instance.inventor.username}
def to_internal_value(self, data):
name = data.get('name', None)
inventor = data.get('inventor', None)
try:
user = User.objects.get(username=inventor)
except Setting.DoesNotExist:
raise serializers.ValidationError('bad inventor')
return Topping(name=name, inventor=user)
class PizzaSerializer(ModelSerializer):
toppings = ToppingRelatedField(many=True)
class Meta:
model = Pizza
fields = ('name', 'toppings')
It seems that since I defined the to_internal_value() for the custom field, it should create/update the many-to-many field automatically. But when I try to create pizzas, I get "Cannot add "": the value for field "pizzatopping" is None" ValueError. It looks like somewhere deep inside, Django decided that the many to many field should be called by the model name. How do I convince it otherwise?
Edit #1: It seems that this might be a genuine bug somewhere in Django or DRF. DRF seems to be doing the right thing, it detects that it is dealing with a ManyToMany field and tries to create toppings from the data using the custom field and add them to the pizza. Since it only has a pizza instance and a field name, it uses setattr(pizza, 'toppings', toppings) to do it. Django seems to be doing the right thing. The __set__ is defined and seems to figure out that it needs to use add() method in the manager. But somewhere along the way, the field name 'toppings' gets lost and replaced by the default. Which is "related model name in lower case".
Edit #2: I have found a solution. I will document it in an answer once I am allowed. It seems that the to_internal_value() method in the RelatedField subclass needs to return a saved instance of a Topping for the ManyToMany thing to work properly. The existing docs show the opposite, a this link (http://www.django-rest-framework.org/api-guide/fields/#custom-fields) the example clearly returns an unsaved instance.
Seems like there is an undocumented requirement. For write operations to work with a custom ManyToMany field, the custom field class to_internal_value() method needs to save the instance before returning it. The DRF docs omit this and the example of making a custom field (at http://www.django-rest-framework.org/api-guide/fields/#custom-fields) shows the method returning an unsaved instance. I am going to update the issue I opened with the DRF team.
I was also trying to return multiple fields as json but getting error unhashable type: 'dict. Finally, I found what's wrong with my approach here - https://github.com/encode/django-rest-framework/issues/5104
RelatedFields generally represent a related object as a single value
(eg, a slug, primary key, url, etc...). If you want to provide a
nested object representation, then you should use a nested serializer.
I have one model that has a ManyToMany Field (let's call it "Options") with another Model
When I create the ModelForm it displays all options.
Is there any way to exclude some option values or to show only some of them?
Here is an example:
models.py
class Options (model.Models):
name = ...
...
class Anything (model.Models):
...
options = ManyToManyField(Options)
values of "Options" in my DB:
["OK",
"OK_2",
"NOT_OK",
"OK_3,
"NOT_OK_2"]
Let's say that I need to show ONLY the "OK" values and hide or not to show the "NOT_OK" values.
Is there any way to do this with ModelForms?
You certainly can filter the queryset for a foreign key field or m2m on the related model by using a Form or more commonly a ModelForm.
The reason doing this at form level is useful is because that filtering could well be based on business logic which is not applicable in all cases and so allows more flexibility than defining it against the model for example.
While you can do this while defining the form fields it is best to do it once the form has been constructed and so it takes place at runtime and not compile time (I have just experienced a few interesting occasions where this has caused me some issues, however that was an earlier version of Django!)
The following ModelForm would do the job:
class AnythingForm(ModelForm):
options = forms.MultipleChoiceField()
def __init__(self, **kwargs):
super(AnythingForm, self).__init__(self, **kwargs)
self.fields['options'].queryset = Option.objects.filter({pass in your filters here...})
class Meta:
model = Anything
You can pass the limit_choices_to parameter to your ManyToMany field:
from django.db.models import Q
class Anything (models.Model):
options = models.ManyToManyField(Options,
limit_choices_to=Q(name__startswith='OK'))
In django 1.7 you can even pass a callable in case if list of choices should be changed dynamically.
I'm working in a django project which I need to list two different models in the same view ordered by date. In order to achieve that I used inheritance to be able to get them all into a generic queryset. My models are:
class Publication(models.model):
title = models.CharField(max_lengh = 200)
pub_date = models.DateTimeField(default = datetime.now)
headline = models.TextField()
class Meta:
abstract = True
#abc.abstractmethod
def say_hello(self):
return
class New(Publication):
author = models.ForeignKey(Author)
source = models.CharField(max_length = 200)
categories = models.ManyToManyField(Category)
url = '/news/'
def say_hello(self):
return "Hello New!!!"
class Opinion(Publication):
writer = models.ForeignKey(Writer)
style = .models.CharField(max_length=3, choices=(('txt', 'Text'), ('glr', 'Galery')))
url = '/opinions/'
def say_hello(self):
return "Hello Opinion!!!"
I'm trying to call the subclass method while iterating through the Publication QuerySet like this:
publications = Publications.objects.all().order_by('-pub_date')
for pub in publications:
pub.say_hello()
url = pub.url
The problem is that my QuerySet is returning Publication objects, so I can't access child attributes and methods, obviously cus I'm dealing with Publication objects. Shouldn't The fact that I've set Publication as an abstract class, avoid the possibility of dealing with Publication objects?. Shouldn't they be prevented from being instantiated? Is there any option for perform perform a QuerySet in Publication class and return a list with child objects?
If no. How would you guys go around this situation? I could really use some tips.
Sounds like it might be appropriate to use multi-table inheritance and django polymorphic:
Multi-table inheritance: https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance
Django polymorphic: http://django-polymorphic.readthedocs.org/en/latest/
Multi-table inheritance in django allows you to have a base model/table which has your base fields. Your subclasses then define the extended fields which are put in their own tables. When you fetch records with querysets from any of the subclasses, you'll get information for each record from both the base model/table and the subclass model/table.
In order to fetch records using the base model's queryset, and get an instance of the appropriate subclass for each result, one option is django polymorphic. I've used it before and it works pretty well. It definitely has its limitations but I'd give it a shot.
Each Publication instance should have either a 'new' attribute or a 'opinion' attribute pointing to one of the two subclasses respectively. Be aware that each instance has only one of this attributes so maybe it's better to try...except access to them.
Well, I will put the code for my solution here which I achieved thanks to #David answer.
As suggested for David, I used django-polymorphic, which is great and simple. But the fact that I already had a populated database, made things a bit complicated. Nothing hard to fix.
First thing I did was to migrate the database with south in order to add the new field (polymorphic_ctype) to my parent model (no field is added to the subclasses).
Then, I used the following code in django shell mode on terminal. (python manage.py shell)
from jornal.models import Publication, New, Opinion
from django.contrib.contenttypes.models import ContentType
ctype_opinion = ContentType.objects.get(model = 'opinion', app_label = 'jornal')
ctype_new = ContentType.objects.get(model = 'new', app_label = 'jornal')
opinions = Opinion.objects.non_polymorphic().all()
news = New.objects.non_polymorphic().all()
for new in news:
new.polymorphic_ctype = ctype_new
new.save()
for opinion in opinions:
opinion.polymorphic_ctype = ctype_opinion
opinion.save()