Display a list of related fields in the admin panel - django

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))

Related

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,
]

How to display table with group same values in the column on Django Admin

I have a Comment table on Django Admin:
models.py
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
item = models.ForeignKey(Listing, on_delete=models.CASCADE)
comment = models.TextField(max_length=250)
datetime = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.item}"
admin.py
class CommentInline(admin.TabularInline):
model = Comment
class ItemAdmin(admin.ModelAdmin):
inlines = [
CommentInline,
]
#admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('item','user','comment','datetime',)
list_filter = ('item',)
And I want to edit this table with the same values item that will display with a single row, and then after clicked on it will display another table contain user comment and datetime .
Thanks so much for any advice to me !!!
If item is a foreign key to one of your other models, you should be able to use an InlineModelAdmin object to create a tabular inline view of the user/comment/datetime using the item's admin view.
See
https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#inlinemodeladmin-objects for reference.
In your case you may end up with something like:
class CommentInline(admin.TabularInline):
model = Comment
fields = ('user', 'comment', 'datetime')
class ItemAdmin(admin.ModelAdmin):
inlines = [
CommentInline,
]
If item is a models.CharField though I don't think you can do it with the Django Admin as provided by Django.

Display admin TabularInline in list_display

I have two models, which are linked reverse by foreign key from my admin point of view:
class Product(models.Model):
name = models.CharField("name", max_length = 128)
class Store(models.Model):
store_id = models.PositiveIntegerField(unique = True)
product = models.ForeignKey(Product, on_delete = models.CASCADE, null = True, blank = True)
And I have an admin view where I want to display the store_id of each product it is available in list_display.
I ask because I found TabularInline - my apporach:
class StoreInline(admin.TabularInline):
model = Store
readonly_fields = ['store_id', "product"]
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ["name",]
inlines = [StoreInline,]
But how would i be able to display the store_id value in list_displays using the Inlines method?
--- workaround (this is only a motviation for my question above), not a solution ---
I worked around by creating a custom method but, I feel like from reading (1, 2, 3) that I have solved it "by hand" and not using a path Django already has. This works:
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ["name", "get_stores"]
def get_stores(self, obj):
return [s.store_id for s in Store.objects.filter(product = obj)]

Django admin show __str__ value in name

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.

django rest framework - Nested serialization not including nested object fields

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.