Get Django admin.ModelAdmin to display join of two tables - python

I have a Django app with the following models.py
from django.db import models
class Order(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
paid = models.BooleanField(default=False)
delivered = models.BooleanField(default=False)
class OrderItem(models.Model):
order = models.ForeignKey(Order,
related_name='items',
on_delete=models.CASCADE)
product = models.ForeignKey(Product,
related_name='order_items',
on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
And in my admin.py, I have this
from django.contrib import admin
#admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ['id', 'first_name', 'last_name', 'email',
'paid', 'delivered']
list_filter = ['paid', 'delivered']
This only shows the Order table.
I would like to join the Order with the OrderItem table and display it in the Django admin. I am not sure this is relevant but for one Order, there could be many OrderItem(s).

As far as I know, you can't show OrderItems in OrderAdmin directly. But you can show Order in OrderItemAdmin, or use InLineModelAdmin to show OrderItems in Order Detail page. Like this:
class OrderItemInline(admin.TabularInline):
model = OrderItem
class OrderAdmin(admin.ModelAdmin):
inlines = [
OrderItemInline,
]
If you still want to display OrderItems (or parts of order item) in admin page, then you can add a method in list_display field, and use that method to fetch OrderItems. Like this:
class OrderAdmin(admin.ModelAdmin):
list_display = (..., 'get_order_items')
def get_order_items(self, obj):
return return ", ".join(obj.items.values_list("pk", flat=True))
get_order_items.short_description = 'Order Items'

Related

How to optimize queries in django-admin? Too many sql queries because of foreign key

I have model of product and category:
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.PROTECT)
name = models.CharField(max_length=255)
In admin.py:
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_editable = ('name', 'category')
When I go to admin page there are too many duplicated SQL queries, all of them getting categories. If i remove category from list_editable, all duplicated queries disappear.
I tried to do this:
def get_queryset(self, request):
qs = super(ProductAdmin, self).get_queryset(request).select_related('category')
return qs
It doesn't work.

A count of ForeignKey

I have class Team, which means a group of users. And the relation with User is One-to-Many.
class Team(models.Model):
member = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
But Team may consist of 2 and N members. I think to write manually is not our way, because it depends on count of people which is always variable.
class Team(models.Model):
member1 = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
member2 = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
member3 = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
How can I do it more elegant and rational? I mean to connect count variable and ForeignKeys associating with users.
upd 15:17 UTC. I still don't understand how to make this. Sorry. So, let's begin to draw.
For example, I wrote simple code
class Event(models.Model):
id_user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
But when I go to admin panel to add Event, I can add only one user. But I need to add a lot of users, who want to participate in the Event and then to divide them into Teams. I think I don't need another class Member. It seems unnecessary. Am I wrong?
As you stated Team model, have ManyToOne relation that means ForeignKey.
But Team may consist of 2 and N members.
You should create two models Team and Member. In Member model, you can store all information related to members such as their age, gender, city, etc.
In Team model, you can store information related to particular team which consists of more than one Member i.e. It has ManyToOne relation with Member.
Models.py:
from django.db import models
from django.contrib.auth.models import User
class Member(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
def __str__(self) -> str:
return f"{self.user.username}"
class Team(models.Model):
member = models.ForeignKey(Member, on_delete=models.SET_NULL, null=True)
Registering in admin site:
admin.py:
#admin.register(Member)
class MemberAdmin(admin.ModelAdmin):
list_display = ['id', 'user'] #Also specify other fields.
#admin.register(Team)
class TeamAdmin(admin.ModelAdmin):
list_display = ['id', 'member'] #Also specify other fields.
Edit:
According to the current picture, a Event can have more than one Team as well as more than one User. So, make two separate Foreign keys for both the models.
models.py
from django.db import models
from django.contrib.auth.models import User
class Team(models.Model):
# The below field is for name of team
name = models.CharField(max_length=200)
# The below field is for member of team.
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
def __str__(self) -> str:
return f"{self.name}"
class Event(models.Model):
# The below field is for name of event.
name = models.CharField(max_length=200)
# The below field is for user of event.
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
# The below is for team of event.
team = models.ForeignKey(Team, on_delete=models.SET_NULL, null=True)
def __str__(self) -> str:
return f"{self.name}"
admin.py
from django.contrib import admin
from .models import Team, Event
#admin.register(Event)
class EventAdmin(admin.ModelAdmin):
list_display = ['id', 'name', 'user', 'team']
#admin.register(Team)
class TeamAdmin(admin.ModelAdmin):
list_display = ['id', 'name', 'user']
views.py
from django.shortcuts import render
from .models import Team, Event
def home(request):
events = Event.objects.all()
return render(request, 'any_app_name/home.html', {'events': events})
home.html or template file:
<body>
<h2>All team information.</h2>
<div>
{% for event in events %}
<h3>{{forloop.counter}}</h3>
<p>Name of event: {{event.name}}</p>
<p>Name of user related to this event: {{event.user.username}}</p>
<p>Name of team related to this event: {{event.team.name}}</p>
<br><hr>
{% endfor %}
</div>
</body>
Create a team with one field team_name:
class Team(models.Model):
team_name = models.CharField(max_length=1000)
and assign users to this team:
class User(models.Model):
this_users_team = models.ForeignKey(Team, null=True, on_delete=models.SET_NULL)
in this case you can assign as many Users to any team, as you want

Django admin: adding objects for foreignkey from other side

so I have these two models
class Recipe(models.Model):
short_description = HTMLField(max_length=400)
likes = models.ManyToManyField(User, blank=True, related_name='recipe_likes')
slug = models.SlugField(blank=True, unique=True)
published_date = models.DateTimeField(blank=True, default=datetime.now)
ratings = GenericRelation(Rating, related_query_name='recipes')
class Ingredient(models.Model):
name = models.CharField(max_length=20)
amount = models.FloatField()
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='recipe_ingredients')
In the admin panel from the recipes section, if I choose a recipe I want to be able to add ingredients for that recipe, what do I need? I think I don't know the right searchterms to use, hope you understand what I mean.
Thanks for the help.
EDIT
This is the solution:
from django.contrib import admin
from .models import Recipe, Ingredient
class IngredientInline(admin.TabularInline):
model = Ingredient
extra = 3
#admin.register(Recipe)
class RecipeAdmin(admin.ModelAdmin):
list_display = ('title',)
search_fields = ('title', )
inlines = [IngredientInline,]
from django.contrib import admin
from .models import Recipe, Ingredient
class IngredientInline(admin.TabularInline):
model = Ingredient
extra = 3
#admin.register(Recipe)
class RecipeAdmin(admin.ModelAdmin):
list_display = ('title',)
search_fields = ('title', )
inlines = [IngredientInline,]
You'll want to read up on InlineModelAdmins:
https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#inlinemodeladmin-objects
When you register your models with a model admin class, add an inlines list.
The documentation is good on this, so please expand your question if you have more detailed questions!

Django Admin: edit view shows no fields at all

I have defined a couple of models in a new Django 1.8 app.
When I visit the Django Admin, the list view (the table where you see one row per each objects instance) works fine.
My surprise is that when I click on any NSGateway item and enter the edit or create page, there are no fields; just the Save buttons.
How can this be possible? All the NSGateway fields are shown as columns in the list view. But the edit view shows no fields!
These are the models:
from django.db import models
class Enterprise(models.Model):
enterprise_id = models.CharField(max_length=40, primary_key=True)
description = models.CharField(max_length=500, null=True)
name = models.CharField(max_length=500)
creationDate = models.DateTimeField()
lastUpdatedDate = models.DateTimeField()
def __unicode__(self):
return self.description
class NSGateway(models.Model):
nsgateway_id = models.CharField(max_length=40, primary_key=True)
creationDate = models.DateTimeField()
description = models.CharField(max_length=500, null=True)
lastUpdatedDate = models.DateTimeField()
personality = models.CharField(max_length=50)
name = models.CharField(max_length=500)
# Each NSG belongs to 1 and only 1 Enterprise.
# Thus, when we delete de Enterprise, we delete its NSG
enterprise = models.ForeignKey(Enterprise, on_delete=models.CASCADE)
def __unicode__(self):
return self.description
This is how the corresponding admin.py:
from django.contrib import admin
from flexwan_import.models import Enterprise, NSGateway
class EnterpriseAdmin(admin.ModelAdmin):
list_display = ('enterprise_id', 'name', 'description', 'creationDate',
'lastUpdatedDate')
search_fields = ['enterprise_id', 'description']
class NSGatewayAdmin(admin.ModelAdmin):
list_display = ('nsgateway_id', 'name', 'description', 'creationDate',
'lastUpdatedDate')
search_fields = ['nsgateway_id', 'description']
admin.site.register(Enterprise, EnterpriseAdmin)
admin.site.register(NSGateway, NSGatewayAdmin)

Django-mptt admin categories

In my Django project I have a model:
class Category(MPTTModel):
name = models.CharField(default='',
max_length=50,
verbose_name='Название')
slug = models.SlugField(default='')
parent = TreeForeignKey('self',
related_name='children',
null=True,
blank=True,
verbose_name='Родительская категория'
)
order = models.PositiveSmallIntegerField(blank=False,
null=False,
default=0,
verbose_name='Порядок')
is_active = models.BooleanField(default=True,
db_index=True,
verbose_name='Отображать на сайте')
class Meta:
verbose_name = 'Категория'
verbose_name_plural = 'категории'
class MPTTMeta:
order_insertion_by = ['order']
If I add the main categories first (one, two, three), and then add subcategories (four in one, five in two, six in three), I would like to see it in the admin panel like this:
-one
--four
-two
--five
-three
--six
But I have this ordering:
-one
-two
-three
--four
--five
--six
What am I doing wrong?
You need to register Category model with MPTTModelAdmin
In your admin.py
from django.contrib import admin
from mptt.admin import MPTTModelAdmin
from .models import Category
admin.site.register(Category, MPTTModelAdmin)
Reference: https://django-mptt.github.io/django-mptt/admin.html
Thanx! It seems that SortableModelAdmin from suit.admin broke an order.
My admin.py was:
from suit.admin import SortableModelAdmin
from mptt.admin import MPTTModelAdmin
from .models import Category, Good
class CategoryAdmin(MPTTModelAdmin, SortableModelAdmin):
mptt_level_indent = 20
list_display = ('name', 'slug', 'is_active', 'order')
list_editable = ('is_active',)
prepopulated_fields = {"slug": ("name",)}
# Specify name of sortable property
sortable = 'order'
admin.site.register(Category, CategoryAdmin)

Categories

Resources