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)]
Related
I have a database of maybe 100 users that have each 5-10k products linked to them. In the admin panel loading that page is really slow because of the many products. So what I want to do is replacing it with a regex or at least a number input field that does not preload all the products:
models:
class Store(models.Model):
name = models.CharField("name", max_length = 128)
user = models.OneToOneField(User, on_delete = models.CASCADE, )
testproduct = models.Foreignkey(Product, on_delete = models.CASCADE, null = True, blank = True)
class Product(models.Model):
data = models.JSONField()
number = models.PositiveIntegerField()
store = models.ForeignKey(Store, on_delete = models.CASCADE)
admin:
class StoreAdmin(admin.ModelAdmin):
list_display = ["name", ...]
raw_id_fields = ["testproduct", ...]
This way I get an id input field on the admin page:
Is there any way I can make it a regex field, so I can search through the data attribute of my products as well as the number attribute?
I think, best way to achieve your goal is to add extra field to your model admin form and override save method for it, try this:
#admin.py
from django import forms
class StoreAdminModelForm(forms.ModelForm):
regex_field = forms.CharField()
def save(self, commit=True):
product_regex = self.cleaned_data.get('regex_field', None)
instance = super(StoreAdminModelForm, self).save(commit=commit)
# put some more regex validations here
if len(product_regex) > 0:
product = Product.objects.filter(data__regex=product_regex).first()
if product:
instance.testproduct = product
else:
raise forms.ValidationError('Product with data satisfying regex was not found!')
if commit:
instance.save()
return instance
class Meta:
model = Store
fields = '__all__'
class StoreAdmin(admin.ModelAdmin):
form = StoreAdminModelForm
raw_id_fields = ["testproduct", ]
fields = ('name ', 'user', 'testproduct', 'regex_field')
readonly_fields = ('user', )
So, the result - you will have special field for regex expression and you will try to get testproduct based on that field (without touching raw_id_field)
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,
]
I'm trying to build a filter that corresponds to the has_images method on my Django admin, but I can't because it strictly says that has_images is not a field of the model. I tried setting it up as a property, but it also didn't work.
I thought about defining has_images as a field and really calculating it, based on the changes on the model, but I think that would be not optimal.
What would be a good solution here?
models.py
class Product(models.Model):
name = models.CharField("Name", max_length=255)
def has_images(self):
return self.images.all().count() > 0
has_images.boolean = True
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="images")
file = models.ImageField("Product Image")
admin.py
class ProductImageInline(admin.TabularInline):
model = ProductImage
fields = ('file',)
extra = 1
class ProductAdmin(VersionAdmin):
list_display = ('id', 'name', 'has_images',)
inlines = (ProductImageInline,)
Expected result:
Can you share the contents of the admin.py file?
Or let me explain it as follows. Add a feature called list_filter = ('images') into the ProductAdmin class you created in admin.py. If this feature doesn't work (I'm not sure as I haven't tried it), if you create an Admin Class for ProductImages directly, you can already view the pictures and the corresponding Product on that page.
----------- EDIT ----------------
This is how I solved the problem.
models.py
from django.db import models
class Product(models.Model):
name = models.CharField("Name", max_length=255)
is_image = models.BooleanField(default=False, editable=False)
def save(self, *args, **kwargs):
if self.images.count():
self.is_image = True
else:
self.is_image = False
super(Product, self).save(*args, **kwargs)
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="images")
file = models.ImageField("Product Image")
def save(self, *args, **kwargs):
super(ProductImage, self).save(*args,**kwargs)
self.product.save()
admin.py
from django.contrib import admin
from .models import *
class ProductImageInline(admin.TabularInline):
model = ProductImage
fields = ('file',)
extra = 1
class ProductAdmin(admin.ModelAdmin):
list_display = ('id', 'name',)
list_filter = ('is_image',)
inlines = (ProductImageInline,)
admin.site.register(Product, ProductAdmin)
Here I added an is_image BooleanField field with False by default. Every time the save method of the Product model runs, it checks whether there is an image in the ProductImage to which the Product model is attached. If there is an image in it, is_image is set as True.
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 have the following models:
class Property(models.Model):
user = models.ForeignKey(User)
id = models.CharField(max_length=20, null=True)
class Property_Value(models.Model):
id = models.ForeignKey(Property)
amount = models.DecimalField(max_digits = 10, decimal_places
How do I access the Property_Value.amount via Property on the admin page?
I have this so far...
class PropertyAdmin(admin.ModelAdmin):
list_display = ('id', 'user', 'property_value')
def property_value(self, obj):
return obj.id.amount
class Meta:
model = Property
admin.site.register(Property, PropertyAdmin)
You're interacting, in that instance, with a Property object (since you defined that in the Meta) - syntax is the same as elsewhere in Django. So it would be obj.property_value.amount. If you're using PyCharm, you can get autocomplete for the field by telling PyCharm what 'obj' is, like so:
def property_value(self, obj: Property):