I have a simple Model like this:
class Artist(models.Model):
class Meta:
verbose_name = "Artist"
verbose_name_plural = "Artists"
name = models.CharField(max_length=128, unique=True, blank=False)
def test_function(self):
return 'xyz'
And Admin:
class ArtistAdmin(admin.ModelAdmin):
list_display = ['name', 'test_function']
search_fields = ['name']
readonly_fields = []
Now in the list view, the function field is verbosed as TEST_FUNCTION:
In a normal field I would use the Field.verbose_name parameter.
How do I achieve that with the function field?
In object terms thinking, I would try to return a mocked CharField instead of a simple string. Would this work somehow?
As per the documentation, you can give the function an attribute called short_description:
class PersonAdmin(admin.ModelAdmin):
list_display = ('upper_case_name',)
def upper_case_name(self, obj):
return ("%s %s" % (obj.first_name, obj.last_name)).upper()
upper_case_name.short_description = 'Name'
If set, the Django Admin will show this description rather than the function name. Even better, change it into _('Name') to allow for different translations!
Related
I have a property inside a Django model, I have to show it inside the serializer. I put the field inside the serializer, but it's not coming up in the response.
class Example(models.Model):
field_1 = models.ForeignKey(
Modelabc, on_delete=models.CASCADE, null=True, related_name="abc"
)
field_2 = models.ForeignKey(
Modelxyz,
on_delete=models.CASCADE,
null=True,
related_name="xyz",
)
name = models.CharField(max_length=25, blank=True)
#property
def fullname(self):
if self.name is not None:
return "%s%s%s" % (self.field_1.name, self.field_2.name, self.name)
return "%s%s" % (self.field_1.name, self.field_2.name)
Serializer is like this:
class ExampleSerializer(serializers.ModelSerializer):
fullname = serializers.ReadonlyField()
class Meta:
model = Example
fields = [
"id",
"fullname",]
When I call the get API for this, the fullname is not being displayed in the api response. What is the issue?
#property attributes are not included in Django Serializer fields as only Django model fields are shown. I normally use the following workaround for this.
Create a SerializerMethodField.
Return object.property from the method.
So, your Serializer class would be:
class ExampleSerializer(serializers.ModelSerializer):
fullname = serializers.SerializerMethodField()
class Meta:
model = OnlineClass
fields = [
"id",
"fullname",
]
def get_fullname(self, object):
return object.fullname
I think, in ExampleSerializer class, the model should be Example not OnlineClass and the fields should contain all the fields inside the model.
I have my models.py
class Restaurant(models.Model):
name = models.CharField(max_length=100, blank=False)
opening_time = models.TimeField(blank=False)
closing_time = models.TimeField(blank=False)
def __str__(self):
return self.name
#property
def is_open(self):
return (
True
if self.opening_time <= datetime.now().time() < self.closing_time
else False
)
And, my serializer.py:
class RestaurantSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Restaurant
fields = ('pk', 'name', 'opening_time', 'closing_time')
I have the is_open property in the model that checks if the restaurant is open. How can I have the is_open property logic run and update this field, when the object is retrieved using a query on when the user makes a GET request to the serializer API.
Right now, it works when the object is created. Is there a retrieve method on the model where I can put this logic?
I was thinking about using Celery to check if it's open, but it sounds like an overkill solution. Of course, I would like this change to affect the serializer, so it should be something done on the model, I would think.
You can add is_open as a SerializerMethodField:
class RestaurantSerializer(serializers.HyperlinkedModelSerializer):
is_open = serializers.SerializerMethodField()
class Meta:
model = Restaurant
fields = ('pk', 'name', 'opening_time', 'closing_time', 'is_open')
def get_is_open(self, instance):
return instance.is_open
I have a many to many field like this:
class Retailer(models.Model):
name = models.CharField(max_length=100, db_index=True)
city_gps = models.ManyToManyField(City, blank=True, related_name='retailers', db_index=True)
def __str__(self):
retailer_city = ""
if self.city_gps:
retailer_city = self.city_gps.all().first().name
return slugify(self.name) + slugify(retailer_city)
I would like the admin to show a combination of the name and all related cities. However, when I set the admin to show this field like this:
class RetailerAdmin(admin.ModelAdmin):
search_fields = ['name']
list_display = ['name', 'city_gps', 'icon_tag', 'logo_tag', 'header_tag']
I get the error:
: (admin.E109) The value of
'list_display[1]' must not be a ManyToManyField.
So, how can I solve this? Is there a way to show the value of the __str__ method in the the admin?
As said in the docs for list_display in Django:
ManyToManyField fields aren’t supported, because that would entail executing a separate SQL statement for each row in the table. If you want to do this nonetheless, give your model a custom method, and add that method’s name to list_display. (See below for more on custom methods in list_display.)
So you can define this custom method either in your models.py or (I think the more explicit way), directly in your admin.py:
class RetailerAdmin(admin.ModelAdmin):
search_fields = ['name']
list_display = ['name', 'icon_tag', 'logo_tag', 'header_tag', 'retailer_city']
def retailer_city(self, obj):
city_gps = obj.city_gps
retailer_city = city_gps.all().first().name if city_gps else ''
return slugify(obj.name) + slugify(retailer_city)
Note that retailer_city is added in list_display.
i'm trying to get nested object fields populated, however the only thing being returned is the primary key of each object (output below):
{
"name": "3037",
"description": "this is our first test product",
"components": [
1,
2,
3,
4
]
}
How do I have the component model's fields populated as well (and not just the PKs)? I would like to have the name and description included.
models.py
class Product(models.Model):
name = models.CharField('Bag name', max_length=64)
description = models.TextField ('Description of bag', max_length=512, blank=True)
urlKey = models.SlugField('URL Key', unique=True, max_length=64)
def __str__(self):
return self.name
class Component(models.Model):
name = models.CharField('Component name', max_length=64)
description = models.TextField('Component of product', max_length=512, blank=True)
fits = models.ForeignKey('Product', related_name='components')
def __str__(self):
return self.fits.name + "-" + self.name
serializers.py
from rest_framework import serializers
from app.models import Product, Component, Finish, Variant
class componentSerializer(serializers.ModelSerializer):
class Meta:
model = Component
fields = ('name', 'description', 'fits')
class productSerializer(serializers.ModelSerializer):
#components_that_fit = componentSerializer(many=True)
class Meta:
model = Product
fields = ('name', 'description', 'components')
#fields = ('name', 'description', 'components_that_fit' )
The documented approach doesn't seem to be working for me, and gives me the following error (you can see the lines following the standard approach commented out in the serializers.py entry above:
Got AttributeError when attempting to get a value for field 'components_that_fit' on serializer 'productSerializer'.
The serializer field might be named incorrectly and not match any attribute or key on the 'Product' instance.
Original exception text was: 'Product' object has no attribute 'components_that_fit'.
Update based on answer
Thanks to #Carlton's answer below, here's what is working for me:
serializers.py was changed and now looks like this:
class productSerializer(serializers.ModelSerializer):
components = componentSerializer(many=True)
class Meta:
model = Product
fields = ('name', 'description', 'components')
By calling the field components_that_fit, you're having the serialiser look for an attribute by that name. (There isn't one, hence your error.)
Two ways to fix it:
Call the field components, but declare it as components = componentSerializer(many=True)
Set source='components' field option when declaring the components_that_fit field.
The reason you get primary keys is that, unless declared explicitly, relations default to PrimaryKeyRelatedField.
I hope that helps.
I want to display the level field of the category to which the product is related on the object's admin page.
class Category(models.Model):
name = models.CharField(max_length=50, default=False)
level = models.IntegerField(help_text="1, 2 ,3 or 4")
class Product(models.Model):
category = models.ForeignKey(Category)
name = models.CharField(max_length=100)
prepopulated_fields = {'slug': ('name',)}
fieldsets = [
('Product Info',{'fields': ['name', 'slug','partno','description']}),
('Categorisation',{'fields': ['brand','category']}),
I have found references to list_filter, but nothing regarding how to show the field.
Does anyone know how to do this?
Define a method on the ModelAdmin class which returns the value of the related field, and include that in list_display.
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'level')
model = Product
def level(self, obj):
return obj.category.level
To show the related field in a ModelAdmin's fieldset, the field must first be declared in readonly_fields.
Define a method that returns the desired value.
Include the method or its name in readonly_fields.
Include the method or its name in its fieldset's "fields" list.
from django.contrib import admin
from .models import MyModel
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
readonly_fields = ['get_parent_name'] # Don't forget this!
fieldsets = [('Parent info', {'fields': ['get_parent_name']} )]
#admin.display(description='Parent')
def get_parent_name(self, obj):
return obj.parent.name
On the Change page, there will be a "Parent info" section with the object's parent's name.
In your admin.py file
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'category__level', 'category')
admin.site.register(Product, ProductAdmin)
Try this.............
The simplest way is to put the level of the Category into the __unicode__ method:
class Category(models.Model):
name = models.CharField(max_length=50, default=False)
level = models.IntegerField(help_text="1, 2 ,3 or 4")
def __unicode__(self):
return u'%s [%d]' % (self.name, self.level)
So the select box will show it.