Foreign key value in Django REST Framework - python

models.py:
class Station(models.Model):
station = models.CharField()
class Flat(models.Model):
station = models.ForeignKey(Station, related_name="metro")
# another fields
Then in serializers.py:
class StationSerializer(serializers.ModelSerializer):
station = serializers.RelatedField(read_only=True)
class Meta:
model = Station
class FlatSerializer(serializers.ModelSerializer):
station_name = serializers.RelatedField(source='station', read_only=True)
class Meta:
model = Flat
fields = ('station_name',)
And I have an error:
NotImplementedError: RelatedField.to_representation() must be implemented.
If you are upgrading from REST framework version 2 you might want ReadOnlyField.
I read this, but it does not help me.
How to fix that?
Thanks!

RelatedField is the base class for all fields which work on relations. Usually you should not use it unless you are subclassing it for a custom field.
In your case, you don't even need a related field at all. You are only looking for a read-only single foreign key representation, so you can just use a CharField.
class StationSerializer(serializers.ModelSerializer):
station = serializers.CharField(read_only=True)
class Meta:
model = Station
class FlatSerializer(serializers.ModelSerializer):
station_name = serializers.CharField(source='station.name', read_only=True)
class Meta:
model = Flat
fields = ('station_name', )
You also appear to want the name of the Station object in your FlatSerializer. You should have the source point to the exact field, so I updated it to station.name for you.

Related

Django related model not updating related object in admin

I have 2 models that look like this:
models.py
class Client(models.Model):
deal = models.ManyToManyField('Deal', related_name="clients")
class Deal(models.Model):
client = models.ManyToManyField(Client, related_name="deals")
Then in the admin, I have inlined the related models to make it easy to make changes regardless of the object type you have open.
admin.py
class ClientInline(admin.TabularInline):
model = Deal.client.through
class DealAdmin(admin.ModelAdmin):
inlines = [ClientInline]
class DealInline(admin.TabularInline):
model = Client.deal.through
class ClientAdmin(admin.ModelAdmin):
inlines = [DealInline]
However, if you add a Client to a Deal and then open the Client detail page, the corresponding deal does not appear. Is there something I'm not connecting?
It is enough to have relation define only in one model. Otherwise you'll have 2 separate tables for separate ManyToMany relation: ClientDeal and DealClient.
What you need to do is to choose which one you need to leave. And probably update Admin inlines according to Django Admin documentation
class Client(models.Model):
deals = models.ManyToManyField('Deal', related_name="clients")
class Deal(models.Model):
pass
Yes, If you're using models.manytoMany() , you have to put it only in one model. no the two
But there's a very good attribute you should use: through
with through attribute you can create a intermediate model. here there's an example:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=200)
groups = models.ManyToManyField('Group', through='GroupMember', related_name='people')
class Meta:
ordering = ['name']
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=200)
class Meta:
ordering = ['name']
def __unicode__(self):
return self.name
class GroupMember(models.Model):
person = models.ForeignKey(Person, related_name='membership')
group = models.ForeignKey(Group, related_name='membership')
type = models.CharField(max_length=100)
def __unicode__(self):
return "%s is in group %s (as %s)" % (self.person, self.group, self.type))
later, you can use your inline admin class!
I just tested this an you were actually really close.
First, #wowkin2 said, you don't want to define a ManyToManyField in both models so I would probably just define it in your Deal model.
Second, replace this:
class DealInline(admin.TabularInline):
model = Client.deal.through
with this:
class DealInline(admin.TabularInline):
model = Deal.client.through
And everything should work.
So, this is what your files should now look like:
models.py
class Deal(models.Model):
client = models.ManyToManyField(Client, related_name="deals")
admin.py
class ClientInline(admin.TabularInline):
model = Deal.client.through
class DealAdmin(admin.ModelAdmin):
inlines = [ClientInline]
class DealInline(admin.TabularInline):
model = Deal.client.through
class ClientAdmin(admin.ModelAdmin):
inlines = [DealInline]

Django prefetch_related in property of inherited model

I am running into an issue with prefetch_related on a property of an inherited model.
In models.py I have:
class Security(models.Model):
name = models.TextField()
...other fields
class Fund(Security):
...other fields
#property
def underlying_security(self):
return self.maps_when_mapped.get().original_security
class SecurityToSecurityMap(models.Model):
original_security = models.ForeignKey(Security, related_name="maps_when_original")
mapped_security = models.ForeignKey(Security, related_name="maps_when_mapped")
and in serializers.py
class UnderlyingSecuritySerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
class FundSerializer(serializers.ModelSerializer):
underlying_security = UnderlyingSecuritySerializer(read_only=True, allow_null=True)
class Meta:
model = Fund
fields = ("id", "name", "underlying_security")
Now, I when I am querying fund objects:
queryset = Fund.objects.prefetch_related("maps_when_mapped__original_security")
Django ignores prefetching. Experimented with `Prefetch, tried moving property to the parent - no luck, still getting hundreds on queries. Any way this can be optimised?
I am using Django 2.2 with Postgres 11
UPDATE 1
After looking at the question, I suspect that problem is when I use .get() in property, it is immediately evaluated but then not sure how to fetch it without evaluation

DRF: Reverse foreign key assignment using an array of primary keys

As stated in this question
With Django REST Framework, a standard ModelSerializer will allow ForeignKey model relationships to be assigned or changed by POSTing an ID as an Integer.
I am attempting to update a reverse relationship of the following format:
class Lesson(models.Model):
name = models.TextField()
class Quiz(models.Model):
lesson = models.ForeignKey(Lesson, related_name='quizzes', null=True)
class LessonSerializer(serializers.ModelSerializer):
quizzes = serializers.PrimaryKeyRelatedField(queryset=Quiz.objects.all(), many=True, write_only=True)
class Meta:
model = Lesson
fields = ('quizzes')
When posting an update containing an array of quiz primary keys using LessonSerializer I get TypeError: 'Quiz' instance expected, got '1'.
Is it possible to assign or change a reverse relationship by POSTing an array of primary keys?
You don't need special field in a serializer, DRF serializers is smart enough to figure out related field from a fields value.
class LessonSerializer(serializers.ModelSerializer):
class Meta:
model = Lesson
fields = ('quizzes', 'name')
And you should pass list of ids, if there is only one value it should be a list anyway.
To solve this you need to create a Quiz instance first before you assign it to Lesson. Below's a small change to your code.
class Lesson(models.Model):
name = models.TextField()
class Quiz(models.Model):
lesson = models.ForeignKey(Lesson, related_name='quizzes', null=True)
class LessonSerializer(serializers.ModelSerializer):
quizzes = serializers.PrimaryKeyRelatedField(queryset=Quiz.objects.all(), many=True, write_only=True)
class Meta:
model = Lesson
fields = ('quizzes')
class QuizSerializer(serializers.ModelSerializer):
class Meta:
model = Quiz
fields = ('name')
Create POST request with with quiz then run your post again

How to rename ForeignKey set field in Django Rest Framework

I am serializing Foreign key set using Django Rest Framework, I have following models:
class Transaction(models.Model):
...
class TransactionStatus(models.Model):
transaction = models.ForeignKey(Transaction)
...
I have a serializer for both of these models, one of them looks like this:
class TransactionSerializer(serializers.ModelSerializer):
transactionstatus_set = TransactionStatusSerializer(many=True, read_only=True)
class Meta:
model = Transaction
depth = 1
fields = ('id', 'transactionstatus_set')
I want to have here a list of transaction statuses from back referenced _set queryset... But transaction_set just seems very awkward name in API for that..
After a quick experimenting I have discovered that this will do the trick:
class TransactionSerializer(serializers.ModelSerializer):
changes = TransactionStatusSerializer(many=True, read_only=True, source='transactionstatus_set')
class Meta:
model = Transaction
depth = 1
fields = ('id', 'changes')
Now I have a list of the statuses linked by foreign key with a nice name...

How can I update only certain fields in a Django model form?

I have a model form that I use to update a model.
class Turtle(models.Model):
name = models.CharField(max_length=50, blank=False)
description = models.TextField(blank=True)
class TurtleForm(forms.ModelForm):
class Meta:
model = Turtle
Sometimes I don't need to update the entire model, but only want to update one of the fields. So when I POST the form only has information for the description. When I do that the model never saves because it thinks that the name is being blanked out while my intent is that the name not change and just be used from the model.
turtle_form = TurtleForm(request.POST, instance=object)
if turtle_form.is_valid():
turtle_form.save()
Is there any way to make this happen? Thanks!
Only use specified fields:
class FirstModelForm(forms.ModelForm):
class Meta:
model = TheModel
fields = ('title',)
def clean_title(self....
See http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#controlling-which-fields-are-used-with-fields-and-exclude
It is common to use different ModelForms for a model in different views, when you need different features. So creating another form for the model that uses the same behaviour (say clean_<fieldname> methods etc.) use:
class SecondModelForm(FirstModelForm):
class Meta:
model = TheModel
fields = ('title', 'description')
If you don't want to update a field, remove it from the form via the Meta exclude tuple:
class Meta:
exclude = ('title',)

Categories

Resources