How to display a foreign key value instead of the id? - python

I have the following models :
class FlightSchedule(models.Model):
tail_number = models.ForeignKey(TailNumber, null=False)
flight_number = models.CharField(max_length=30, null=False)
flight_group_code = models.ForeignKey(FlightGroup, null=False)
origin_port_code = models.ForeignKey(Port, null=False, related_name="Origin")
destination_port_code = models.ForeignKey(Port, null=False, related_name="Destination")
flight_departure_time = models.TimeField()
start_date = models.DateField()
end_date = models.DateField()
def __unicode__(self):
return u'%s' % self.flight_number
class Meta:
verbose_name_plural = "Flight Schedule"
class FlightScheduleDetail(models.Model):
flight_date = models.CharField(max_length=30, null=False)
flight_number = models.ForeignKey(FlightSchedule, null=False, related_name="flight_number_schedule")
route_id = models.CharField(max_length=30, null=False, unique=True)
flight_status = models.ForeignKey(Status, null=True, default=1)
def __unicode__(self):
return u'%s' % self.route_id
class Meta:
verbose_name_plural = "Flight Schedule Details"
and the serializer is as below :
class FlightScheduleDetailSerializer(serializers.ModelSerializer):
class Meta:
model = FlightScheduleDetail
fields = '__all__'
class FlightScheduleSerializer(serializers.ModelSerializer):
flight_number_schedule = FlightScheduleDetailSerializer(many=True)
class Meta:
model = FlightSchedule
fields = ['tail_number', 'flight_number', 'origin_port_code', 'destination_port_code', 'flight_departure_time',
'flight_number_schedule']
Here tail_number , flight_number is a foreign key. When I create an API, I get the response as the id of the fields. How can I display the name in the json?
My views.py is as below :
#api_view(['GET'])
def flight_schedule(request):
schedule = FlightSchedule.objects.all()
serializer = FlightScheduleSerializer(schedule, many=True)
return Response(serializer.data)

You can define the source with field_name in your serializer as follows.
I have used source='TailNumber.number'. Please use the right field_name in place of number
class UserProfileSerializer(serializers.ModelSerializer):
tail_number = serializers.CharField(source='TailNumber.number', read_only=True)
flight_number = ....(change as above)
class Meta:
model = FlightSchedule
fields = ['tail_number', 'flight_number', 'origin_port_code', 'destination_port_code', 'flight_departure_time',
'flight_number_schedule']

You could simply add them as if they were attributes.
flight_number_str = serializers.ReadOnlyField(source='flight_number.flight_number')
First flight_number is the attribute of FlightScheduleDetail, then the one of FlightSchedule
and then add it to the list of fields fields = [..., 'flight_number_str']
Otherwise you may have a look at nested relationships in DRF which can offer more possibilities also.

Another alternative is to use the depth option in a serializer. It is to specify nested serialization - doc
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
depth = 1
If users is a foreign key or manytomany key the serializer will display the users as an object and not as a key.
The depth option should be set to an integer value that indicates
the depth of relationships that should be traversed before reverting
to a flat representation.

Related

count total object in foreign key - drf

i want to count comments for every single Post
in models.py:
class Post(models.Model):
body = models.TextField(max_length=10000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
liked_by = models.ManyToManyField(User, blank=True, related_name='liked_by')
class Meta:
ordering = ['-date']
class Comment(models.Model):
body = models.TextField(max_length=1000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
class Meta:
ordering = ['-date']
in serializers.py:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
#comments = CommentSerializer()
user = UserSerializers()
total_likes = serializers.SerializerMethodField()
liked_by = SimpleUserSerializer(many=True, read_only=True)
total_comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('body','date','user', 'total_likes', 'liked_by','total_comments')
def get_total_likes(self, instance):
return instance.liked_by.count()
def get_total_comments(self, instance):
return instance.comments.count()
when i run this code, it shows, AttributeError: 'Post' object has no attribute 'comments'.
how do i count comments of a post?
Since you haven't configured the related_name, Django uses the default related_name and hence you should access the reveres FK using comment_set instead of comments
Thus, the get_total_comments(...) method should look like
def get_total_comments(self, instance):
return instance.comment_set.count()
Reference
What is related_name used for in Django?

Django Rest Framework: How to get instance of related foreign key

Note: IF INFORMATION BELOW IS NOT CLEAR TO UNDERSTAND - PLEASE ASK ME, I WILL UPDATE AND POST INFORMATION YOU NEED | It is important for me
In Warehouse(models.Model) I have amount attribute and
in ChosenProduct(models.Model) - quantity
I'm trying to get amount in Warehouse through chosen_products instance in App_formSerializer to add the quantity of chosen_product
But I can not get the chosen_products objects from instance
--> below Out:
class WarehouseSerializer(serializers.ModelSerializer):
category_name = serializers.ReadOnlyField(
source='category_product.category_name')
posted_user = serializers.ReadOnlyField(
source='posted_user.username')
class Meta:
model = Warehouse
fields = ['id', 'category_product', 'category_name', 'condition',
'product_name', 'amount', 'barcode', 'f_price', 'created_at', 'updated_at', 'posted_user']
class ChosenProductSerializer(serializers.ModelSerializer):
product_info = WarehouseSerializer(source='product', read_only=True)
period_info = Product_periodSerializer(source='period', read_only=True)
class Meta:
model = ChosenProduct
exclude = ('app_form',)
class App_formSerializer(serializers.ModelSerializer):
chosen_products = ChosenProductSerializer(many=True)
def update(self, instance, validated_data):
instance.terminated = validated_data.get('terminated', instance.terminated)
if instance.terminated == True :
print('-----------TRUE--------------------')
print(instance.chosen_products)
print('-----------PRINT--------------------')
instance.save()
return instance
class Meta:
model = App_form
fields = '__all__'
Out
-----------TRUE--------------------
creditapi.ChosenProduct.None
-----------PRINT--------------------
QUESTION UPDATED
models.py
class Warehouse(models.Model):
category_product = models.ForeignKey(
Category_product, on_delete=models.CASCADE)
product_name = models.CharField(max_length=200, unique=True)
condition = models.BooleanField(default=False)
amount = models.IntegerField()
barcode = models.BigIntegerField()
f_price = models.CharField(max_length=255, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
posted_user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
def __str__(self):
return self.product_name
class App_form(models.Model):
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,12}$', message="Phone number must be entered in the format: '998981234567'. Up to 12 digits allowed.")
terminated = models.BooleanField(default=False)
name = models.CharField(max_length=150)
phone_number = models.CharField(validators=[phone_regex], max_length=13)
def __str__(self):
return self.surname
class ChosenProduct(models.Model):
product = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
quantity = models.IntegerField()
app_form = models.ForeignKey(App_form, on_delete=models.CASCADE, related_name='chosen_products')
def __str__(self):
return self.product.product_name
If you write instance.chose_products you access the manager, not the QuerySet that contains the items. You can use .all() to obtain the QuerySet with all the objects:
print(instance.chosen_products.all())
If you access a ForeignKey in reverse, you have a manager, since zero, one, or more elements can refer to the instance.
You can for example aggregate over the chose_products, for example if you want to retrieve the number of related chose_products, you can use:
print(instance.chosen_products.count())
I would however advise not store (aggregated) data in the App_form, but aggregate data when you need it. Data duplication is an anti-pattern, and it turns out it is hard to keep data in sync.

Django admin export issue

admin.site.register(RegistrationOTP)
class RegistrationOTPResource(resources.ModelResource):
class Meta:
model = RegistrationOTP
export_order = ('country','phone_number','otp','created_date','is_verified')
class RegistrationOTPAdmin(ImportExportModelAdmin):
resource_class = RegistrationOTPResource
here is models.py
class RegistrationOTP(models.Model):
country = models.ForeignKey(Country,on_delete=models.CASCADE, related_name='otp_country', default=1)
phone_number = models.CharField(max_length=13)#, unique=True, validators=[phone_regex],
otp = models.PositiveIntegerField()
created_date = models.DateTimeField(auto_now=True)
is_verified = models.CharField(choices=YES_OR_NO, default='no', max_length=10)
def __unicode__(self):
return '%s : %s'% (self.phone_number, self.otp)
Here i am doing as per documentation but it is not showing export csv option in admin
class RegistrationOTPResource(resources.ModelResource):
class Meta:
model = RegistrationOTP
fields = '__all__'
export_order = ('id','country','phone_number','otp','created_date','is_verified')
try this, i guess due to missing fields section, it is not working

How to serialize a one to many relation in django-rest using Model serializer?

These are my models and serializers. I want a representation of Question Model along with a list of people the question was asked to.
I am trying this:
#api_view(['GET', 'PATCH'])
def questions_by_id(request,user,pk):
question = Question.objects.get(pk=pk)
if request.method == 'GET':
serializer = QuestionSerializer(question)
return Response(serializer.data)
But I get an empty dictionary ({}). However when I remove the asked field from QuestionSerializer I get a complete representation of Question along with Places serialized nicely. What am I missing ?
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
class Question(BaseModel):
title = models.CharField(max_length=200, null=False)
places = models.ManyToManyField(Place, blank=True)
answered = models.BooleanField(default=False)
class AskedTo(BaseModel):
ques = models.ForeignKey(Question, on_delete=models.CASCADE)
to_user = models.ForeignKey(settings.AUTH_USER_MODEL)
replied = models.BooleanField(default=False)
class Place(models.Model):
g_place_id = models.CharField(max_length=20,primary_key=True)
json = models.TextField(null=True)
name = models.CharField(max_length=40)
I figured it out. There were two errors.
Changed this:
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
to this (notice the change in fields, fields on model and serializer didn't match)
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'replied')
Secondly, I needed to define any extra fields outside class Meta
class QuestionSerializer(serializers.ModelSerializer):
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
class Meta:
model = Question
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
Notice the change in definition of places and asked.
In my case, I have this models.py:
class Section(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True)
user = models.ForeignKey(Profile, related_name="sections", on_delete=models.CASCADE)
class Feed(models.Model):
title = models.CharField(max_length=500)
link_rss = models.URLField(max_length=500)
link_web = models.URLField(max_length=500)
description = models.TextField(blank=True)
language = models.CharField(max_length=50, blank=True)
logo = models.URLField(blank=True)
sections = models.ManyToManyField(Section, related_name="feeds")
And I completed the serializers.py this:
class FeedSerializer(serializers.ModelSerializer):
sections = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Feed
fields = '__all__'
class SectionSerializer(serializers.ModelSerializer):
feeds = FeedSerializer(many=True, read_only=True)
class Meta:
model = Section
exclude = ('user',)

Django-autocomplete-light returns object names instead of corresponding field values

My problem whith django-autocomplete-light (dal 3) is that in the admin, instead of showing the choices corresponding tom my designated field (i.e. birth_nation see forms.py section), I always get a list of the str values of my queryset objects (see #models.py section) which is actually the last_name field.
# models.py
class MyModel(models.Model):
id_name = models.CharField(primary_key=True, max_length=255)
first_name = models.CharField(max_length=255, blank=True, null=True)
middle_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255)
birth_city = models.CharField(max_length=255, blank=True, null=True)
birth_nation = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.last_name
class Meta:
managed = False
db_table = 'mytable'
# forms.py
class MyModelForm(forms.ModelForm):
birth_nation = forms.ModelChoiceField(
queryset=MyModel.objects.all(),
widget=autocomplete.ModelSelect2(url='country-autocomplete',
attrs={'data-minimum-input-length': 2}
)
)
class Meta:
model = MyModel
fields = ('__all__')
# views.py
class MyModelAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
if not self.request.user.is_authenticated():
return MyModel.objects.none()
qs = MyModel.objects.all()
if self.q:
qs = qs.filter(birth_nation__istartswith=self.q)
return qs
Obviously, I want to get my choices that correspond to the birth_nation, what is wrong in my code?
try this:
def __str__(self):
return self.birth_nation
Autocomplete return value is a query object. If you want to see specific field in the object, write that field in your model class or change your autocomplete class return value.

Categories