Django admin show __str__ value in name - django

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.

Related

Display a list of related fields in the admin panel

I have two related models.
class Refbook(models.Model):
code = models.CharField(max_length=100, unique=True)
name = models.CharField(max_length=300)
description = models.TextField()
class VersionRefbook(models.Model):
refbook_id = models.ForeignKey('Refbook',
on_delete=models.CASCADE,
related_name='versions')
version = models.CharField(max_length=50)
date = models.DateField()
When I edit a Refbook instance in the admin panel, I want the read-only list of available versions of this Refbook instance to be displayed on the same page.
I know that it is possible to output them through TabularInline . And it seems that there is a read-only property here.
Maybe there is a way to display just a list in a column or row separated by commas?
Now, I have this code in admin.py:
#admin.register(Refbook)
class RefbookAdmin(admin.ModelAdmin):
list_display = ['id', 'code', 'name']
I tried to create a "get_versions(self)" method in models.py in Refbook class, in which I received a queryset using related_name. But I can't display it in the admin panel. Or is it still correct to do this using the model.ModelAdmin parameters?
You can do something like this:
#admin.register(Refbook)
class RefbookAdmin(admin.ModelAdmin):
list_display = ['id', 'code', 'name', 'versions']
def versions(self, obj):
return ', '.join(obj.versions.values_list('version', flat=True))

Display information about linked models fields in the django admin

These are my models:
class Partner(models.Model):
name = models.CharField(max_length=200, verbose_name="Organisation name")
class ResearchActivity(models.Model):
title = models.CharField(max_length=200)
partner = models.ManyToManyField(ActivityPartner, blank=True)
I'd like, in the Django administration forms, to have a field in my Partner edit form representing the ResearchActivity linked to that Partner.
Can this be achieved by adding a field to my Partner model (say, naming it linked_partner) and then edit my admin.py like so:
#admin.register(ActivityPartner)
class ActivityPartnerAdmin(admin.ModelAdmin):
search_fields = ['academic',]
autocomplete_fields = ['partnership_type', 'relationship_type', 'academic_links']
def get_changeform_initial_data(self, request):
return {'live_contract': ResearchActivity.objects.all().filter(linked_partner__id=request.ResearchActivity.partner.id)}
?
I have just come across in the display() decorator, new from Django 3.2. With it, I can simply do:
#admin.register(ActivityPartner)
class ActivityPartnerAdmin(admin.ModelAdmin):
search_fields = ['academic',]
autocomplete_fields = ['partnership_type', 'relationship_type', 'academic_links',]
readonly_fields = ('get_ra',)
#admin.display(description='Live contract(s)')
def get_ra(self, obj):
return list(ResearchActivity.objects.filter(partner=obj.id))
to achieve what I want.
If I also wanted to edit those ManyToMany relations, I can use the inlines option:
class LiveContractsInline(admin.TabularInline):
model = ResearchActivity.partner.through
#admin.register(ActivityPartner)
class ActivityPartnerAdmin(admin.ModelAdmin):
inlines = [
LiveContractsInline,
]

Setting verbose name of function field in Django

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!

Can I display second level up related fields in Django Admin?

Lets say I have a three tier model as below, where posts are in categories which are then in categories. I'd like to get the Posts page in the Admin panel to display both the sub category and category that each post belongs to. And ideally let me filter by both. So far I can only get the Admin panel to work up one relationship level. So I can display and filter by the sub_category but when I add the category I get an error.
models.py:
class Category(models.Model):
name=models.CharField(max_length=10)
def __str__(self):
return self.name
class Subcategory(models.Model):
category=models.ForeignKey(Category,on_delete=models.CASCADE,null=True)
name=models.CharField(max_length=10)
def __str__(self):
return self.name
class Posts(models.Model):
title=models.CharField(max_length=15)
sub_category=models.ForeignKey(Subcategory,on_delete=models.CASCADE,null=True)
def __str__(self):
return self.title
Admin.py:
class PostsAdmin(admin.ModelAdmin):
ordering = ('name',)
list_display = ('name','id','sub_category', 'sub_category__category.name', )
exclude = ('sort',)
list_filter = ['sub_category','sub_category__category']
search_fields = ['name']
admin.site.register(Posts, PostsAdmin)
Error is something like this:
<class 'journal.admin.PostsAdmin'>: (admin.E108) The value of 'list_display[4]' refers to 'sub_category__category', which is not a callable, an attribute of 'PostsAdmin', or an attribute or method on 'journal.Admin'.
You need to add a method on your admin class that returns the category for each post then reference this in list_display
class PostsAdmin(admin.ModelAdmin):
list_display = ('name','id','sub_category', 'category')
def category(self, obj):
return obj.sub_category.category.name
You could also add this method to the post model rather than the model admin

Displaying the model's__unicode__ in django admin

I would like to display the model's username in Django Admin interface but not very sure how to do it..
The models.py:
class Adult(models.Model):
user = models.OneToOneField(User)
fullname = models.CharField(max_length=100,
blank=True)
def __unicode__(self):
return self.user.username
Admin.py:
class AdultAdmin(admin.ModelAdmin):
list_display = ('??', 'Student_Name',)
search_fields = ['??',]
admin.site.register(Adult, AdultAdmin)
What should go inside the ?? above ? I would like to display the unicode or the self.user.username? How do i do it? Need some guidance...
From the list_display documentation there are four things you can add there:
A field
Some method (a callable) that accepts one variable that is the instance for which the row is being displayed.
A string that is the name of a method or attribute defined in the model class.
A string that is the name of a method that is defined in ModelAdmin.
For your case we need #3 for list_display.
For search_fields its easier as you can use follow notation (__) to do lookups.
In the end we come up with this:
class AdultAdmin(admin.ModelAdmin):
list_display = ('__unicode__', 'Student_Name',)
search_fields = ['user__username']