Background:
I have the following models for a visitor management app. Each site can have multiple visitors and each visitor can visit multiple sites. I am unable to find a way to show a list of visitors on each site or show a list of sites a visitor has visited. I have removed unnecessary fields.
.models.py
class Site(models.Model):
name = models.CharField(max_length=100, unique=True)
accomodation = models.BooleanField(default=False)
visitors = models.ManyToManyField('Visitor', blank=True)
class Visitor(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
email = models.EmailField(max_length=200, null=False, blank=False, unique=True)
...
admin.py
class AdminArea(admin.AdminSite):
vms_admin = AdminArea(name='vms_admin')
#admin.register(Site, site=vms_admin)
class SiteAdmin(admin.ModelAdmin):
fieldsets = (*removed*)
list_display = [*removed*]
list_filter = (*removed*)
#admin.register(Visitor, site=vms_admin)
class VisitorAdmin(admin.ModelAdmin):
fieldsets = (*removed*)
list_display = [*removed*]
list_filter = (*removed*)
Django Admin
This is how the list of sites looks:
Django Admin Site List
This is how the list of visitors look like:
Django Admin Visitor List
Question
How do I show the list of visitors for each site and vice versa?
For example:
If I click on Gingin Gravity Precinct, I want to see the list of visitors associated with it, in a table, below "site details". Similar to the list of visitors shown, but specific to the site.
Django Admin Specific Site
First of all, I would make the related_name explicit in model Site. This is not strictly necessary, there is a default backward reference created by Django, I just never remember the name of that, so I like to be explicit.
visitors = models.ManyToManyField('Visitor', blank=True, related_name='sites')
I assume you want to see the list of sites when you click a single Visitor, in other words, on the visitor detail admin page. In that case, all you need to do is to specify the field sites in the VisitorAdmin. Vice versa to display visitors in the SiteAdmin. You will get a list of all visitors then, in which the active ones are selected.
class SiteAdmin(Admin):
fieldsets = (
('Visitors', {'fields': ('visitors')})
)
If you want to display a (possibly long) list of sites in the visitor list admin page, what you can do is to define a property on the Visitor model, and add that property to the list_fields.
class Visitor(Model):
#property
def list_of_sites(self):
return self.sites.all().values_list(name, flat=True)
class SiteAdmin(Admin):
list_display = ['name', 'accomodation', 'list_of_sites']
Related
Use case:
Want to let an admin create favourite relationships between users.
To do this, I am creating a model called Favourites.
class Favourite(models.Model):
user = models.ForeignKey(to=CustomUser, on_delete=models.CASCADE)
otheruser = models.IntegerField()
However, both user and otherusers are both objects in CustomUsers. In the admin console, when adding a favourite I get a list of users, but I do not get a list of other users obviously.
What model field can I use so that when adding a favourite I get a list of users, and when choosing the otheruser that is also a list of users?
It makes more sense here to add a ManyToManyField [Django-doc] in your CustomUser, so:
class CustomUser(models.Model):
# …
favorites = models.ManyToManyField(
'self',
symmetrical=False,
related_name='fans'
)
I have a model OrderPage which is manytomany to Site. In Django admin, I want to restrict the selection of sites(Sites which belong to existing OrderPage can not be selected again). Can I do it with unique_together ? I get an error with following model ManyToManyFields are not supported in unique_together
class OrderPage(models.Model):
description = models.CharField(max_length=255, blank=False)
sites = models.ManyToManyField(Site)
class Meta:
unique_together = (('id', 'sites'),)
class Order(models.Model):
order_page = models.ForeignKey(OrderPage)
class OrderPageAdmin(admin.ModelAdmin):
filter_horizontal = ('sites',)
admin.site.register(OrderPage, OrderPageAdmin)
If an Site can have only one OrderPage, you don't need to worry about unique_together.
Ideally you should subclass Site and use a ForeignKey from that to OrderPage. That would natively give you what you're looking for: each site would be able to have one OrderPage, and each OrderPage multiple Sites. This would be the cleanest but you would have to use your subclass throughout the program in place of the original Site which might be more work than you want right now.
class BetterSite(Site):
order_page = models.ForeignKey('OrderPage')
The dirtier way is to keep your M2M and just set the site as unique, since there should only ever be one entry on each site in the M2M table. You would use a 'through' table so you could set the custom uniqueness value:
class OrderPage(models.Model):
description = models.CharField(max_length=255, blank=False)
sites = models.ManyToManyField(Site, through='OrderPageToSite')
class OrderPageToSite(models.Model):
order_page = models.ForeignKey(OrderPage)
site = models.ForeignKey(Site, unique=True)
(Note that I've left these simple but in your FK fields you should also consider setting on_delete and related_name)
I can add Criterias to a place. How can I add Places to a criteria?
Models:
class Criterias(models.Model):
name = ...
class Places(models.Model):
name = ...
class PlacesToCriterias(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
criteria_group = models.ForeignKey(Criterias)
Admin - PLACES part:
class PlaceCriteriasInlineAdmin(GenericTabularInline):
model = PlacesToCriterias
class PlacesAdmin(admin.ModelAdmin):
inlines = [PlaceCriteriasInlineAdmin]
admin.site.register(Places, PlacesAdmin)
In this case, when I open Places admin change page, I can add Criterias items to my 'place'.
Admin - CRITERIAS part:
class CriteriaPlacesInlineAdmin(GenericTabularInline):
model = PlacesToCriterias
class CriteriasAdmin(admin.ModelAdmin):
inlines = [CriteriaPlacesInlineAdmin]
admin.site.register(Criterias, CriteriasAdmin)
In this case, when I open Criterias admin change page, I CAN NOT add Places item to my 'criteria', because instead of possible places I see criterias.
How to get Places items at Criterias admin page?
It was quite easy. GenericTabularInline must be changed to admin.TabularInline
class CriteriaPlacesInlineAdmin(admin.TabularInline):
model = PlacesToCriterias
class CriteriasAdmin(admin.ModelAdmin):
inlines = [CriteriaPlacesInlineAdmin]
admin.site.register(Criterias, CriteriasAdmin)
If anyone needs to get dropdown list with selected object, instead of content_type and object_id fields, the solution is here.
My model is:
class CustomerAccount(models.Model):
name = models.CharField(max_length=50)
class MyUser(AbstractUser):
customer_account = models.ManyToManyField(CustomerAccount, related_name='users', blank=True)
default_customer_account = models.ForeignKey(CustomerAccount, related_name='users_using_default_account', null=True, blank=True)
I want to display in the admin interface of the CustomerAccount this sort of thing:
I don't need to add a MyUser in the CustomerAccount interface.
Most SO questions and docs are related to show an Inline class in the admin, but I don't need it.
How should I do?
The functinality shown above you get by making adding the desired field to the filter_horizontal list (docu).
I have 3 models as such:
class Customer (models.Model):
name = models.CharField (max_length = 20)
class Vehicle (models.Model):
make = models.CharField (max_length = 20)
customer = models.ForeignKey(Customer)
class Appointment (models.Model):
customer = models.ForeignKey (Customer)
vehicle = models.ForeignKey (Vehicle)
In the Appointment admin site, it currently shows me two dropdown lists, one showing all customers and the other showing all vehicles.
I want to implement it such that I first pick a customer, and then the second dropdown list is populated with a list of vehicles the customer owns. I know how to do this in a standard template with jQuery, but I can't find any documentation about how to do this in the admin site.
https://github.com/digi604/django-smart-selects
EDIT: There's also a small app which provides a widget that you can use in django admin, and which doesn't require changes to the model (ChainedForeignKey etc.).
https://github.com/runekaagaard/django-admin-flexselect