Django Admin list_display product list - python

I new in django and I trying modifying a project to learn.
I've two classes in a model ('Order' and 'OrderItem'), the class OrderItem stores all items selected in a client order.
models.py
class Order(models.Model):
STATUS_CHOICES = (
(0, 'Waiting Payment'),
(1, 'Completed'),
(2, 'Canceled'),
)
PAYMENT_OPTION_CHOICES = (
('deposit', 'deposit'),
('paypal', 'Paypal'),
)
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='User')
status = models.IntegerField(
'Situation', choices=STATUS_CHOICES, default=0, blank=True
)
payment_option = models.CharField(
'Payment Options', choices=PAYMENT_OPTION_CHOICES, max_length=20,
default='deposit'
)
created = models.DateTimeField('Created in', auto_now_add=True)
modified = models.DateTimeField('Modified in', auto_now=True)
objects = OrderManager()
class Meta:
verbose_name = 'Order'
ordering = ('-created',)
def __str__(self):
return 'Order #{}'.format(self.pk)
def products(self):
products_ids = self.items.values_list('product')
return Product.objects.filter(pk__in=products_ids)
def total(self):
aggregate_queryset = self.items.aggregate(
total = models.Sum(
models.F('price') * models.F('quantity'),
output_field = models.DecimalField()
)
)
return aggregate_queryset['total']
class OrderItem(models.Model):
order = models.ForeignKey(Order, verbose_name='Order', related_name='items')
product = models.ForeignKey('event.Product', verbose_name='Product')
quantity = models.PositiveIntegerField('Quantity', default=1)
price = models.DecimalField('Price', decimal_places=2, max_digits=8)
class Meta:
verbose_name = 'Order Item'
def __str__(self):
return '{}'.format(self.product)
In the django admin I can show all Orders and when I click to see more I see all products on this order, but my problem is, I can't list this products of class OrderItem in my list_display, how can I do that?
admin.py
class OrderItemInline(admin.StackedInline):
model = OrderItem
fields = ['product']
readonly_fields = ['product',]
extra = 0
max_num = 0
class OrderAdmin(admin.ModelAdmin):
model = Order
inlines = [ OrderItemInline, ]
readonly_fieldsets = (
(None, {
'fields': ('user','status','order','created')
}),
)
readonly_fields = ['user','status','payment_option']
search_fields = ['user__name', 'user__email']
list_filter = ['status', ]
list_display = ['pk','user','status','created','product']
ordering = ('-created',)
admin.site.register(Order, OrderAdmin)

That's an easy task. Inside your OrderAdmin class, remove the product from the display_list list and add a string that will be the name of a method/callable, say list_products. Now, list_display will show the returned value of that function.
For example, define a list_products method inside the OrderAdmin class.
from django.utils.html import mark_safe
class OrderAdmin(admin.ModelAdmin):
list_display = ['pk', 'user', 'status', 'created', 'list_products']
def list_products(self, obj):
# each obj will be an Order obj/instance/row
to_return = '<ul>'
# I'm assuming that there is a name field under the event.Product model. If not change accordingly.
to_return += '\n'.join('<li>{}</li>'.format(pro_name) for prod_name in obj.items.values_list('product__name', flat=True))
to_return += '</ul>'
return mark_safe(to_return)

That's an easy task. Inside your OrderAdmin class, remove the product from the display_list list and add a string that will be the name of a method/callable, say list_products. Now, list_display will show the returned value of that function.
For example, define a list_products method inside the OrderAdmin class:
from django.utils.html import mark_safe
class OrderAdmin(admin.ModelAdmin):
list_display = ['pk', 'user', 'status', 'created', 'list_products']
def list_products(self, obj):
# each obj will be an Order obj/instance/row
to_return = '<ul>'
# I'm assuming that there is a name field under the event.Product model. If not change accordingly.
to_return += '\n'.join('<li>{}</li>'.format(pro_name) for prod_name in obj.items.values_list('product__name', flat=True))
to_return += '</ul>'
return mark_safe(to_return)

I got it, I don't know if it's the rigth one.
I'm create a list first.
def list_products(self, obj):
products = []
for prod_name in obj.items.values_list('product__name', flat=True):
products.append(''.join(prod_name))
to_return = '<br/>'.join(products)
return mark_safe(to_return)

Related

Filtering Foreign key choices in Django serializer

I have a Django model that looks like the code below. At the moment, when I am using django rest framework to create a new menu instance, the dish column contains options created by all users on the platform.
How should I go about filtering the dish column so it only has options created by the user?
Should I be doing it in the views or serializer?
Thank you for the response in advance.
class Dish(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE, null=True, blank=True)
title = models.CharField(max_length=280)
description = models.CharField(max_length=280)
image = models.ImageField(upload_to='static/images/post_image',
default='static/images/post_image/default.jpg')
def __str__(self):
return f'{self.title}'
def get_image_url(self, obj):
return obj.image.url
class Menu(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE, null=True, blank=True)
title = models.CharField(max_length=280)
description = models.CharField(max_length=280)
dish = models.ManyToManyField(Dish)
price = models.SmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(10000)], default=None)
def __str__(self):
return f'{self.title}'
This is how I ended up doing it for those who have the same problems in the future.
class UserDishForeignKey(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
user = self.context['request'].user
return Dish.objects.filter(user=user)
class MenuCreateSerializer(serializers.ModelSerializer):
dish = UserDishForeignKey(many=True)
class Meta:
model = Menu
fields = ['title', 'description', 'dish', 'price', ]
read_only_fields = ['user', ]
def get_user(self, obj):
return str(obj.user.username)
Assuming that you have an user object, you can get all dishes associated to that user like this:
user.dish_set
If you want to find all menu's that are having particular menus by dish's owner. That can be done like
Menu.objects.filter(dish__user=user)
Placement of this depends on what you are trying to achieve. If you want to validate the input, placement should be in serializer
class UserDishForeignKey(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
user = self.context['request'].user
return Dish.objects.filter(user=user)
class MenuCreateSerializer(serializers.ModelSerializer):
dish = UserDishForeignKey(many=True)
class Meta:
model = Menu
fields = ['title', 'description', 'dish', 'price', ]
read_only_fields = ['user', ]
def get_user(self, obj):
return str(obj.user.username)

search models in django admin

There is a model of orders, where new orders with information fall, photo1
Part of the information is the model of sneakers, sometimes these sneakers need to be corrected, now this is done in the form of TabularInline, photo2 and photo3, there are a lot of sneakers and it takes a very long time to scroll through this drop-down list, is there a way to make a search by entering text, like search_fields?
admin.py
class SuitItemInOderInline(admin.TabularInline):
model = SuitInOder
extra = 0
class SuitOrderAdmin(admin.ModelAdmin):
inlines = [SuitItemInOderInline]
list_display = ('inst', 'created')
list_filter = ('status',)
class Meta:
model = OrderSuit
admin.site.register(OrderSuit, SuitOrderAdmin)
models.py
class SuitInOder(models.Model):
order = models.ForeignKey(OrderSuit, on_delete=models.CASCADE, verbose_name='Заказ', related_name='ordered_item')
item = models.ForeignKey(SuitItem, on_delete=models.CASCADE, verbose_name='Костюм', related_name='spb_item')
def __str__(self):
return f'{self.order} | {self.item.model.id} --- {self.item}'
def save(self, *args, **kwargs):
not_available = ['new', 'in_progress', 'completed']
if self.order.status in not_available:
self.item.in_stock = False
else:
self.item.in_stock = True
self.item.save(force_update=True)
super(SuitInOder, self).save(*args, **kwargs)
def delete(self):
self.item.in_stock = True
self.item.save(force_update=True)
super(SuitInOder, self).delete()
class Meta:
verbose_name = 'Костюм в заказе'
verbose_name_plural = 'Костюмы в заказе'
class OrderSuit(models.Model):
NEW = 'new'
IN_PROGRESS = 'in_progress'
COMPLETED = 'completed'
CANCELED = 'canceled'
choises = (
(NEW, 'Новый'),
(IN_PROGRESS, 'В работе'),
(COMPLETED, 'Выполнен'),
(CANCELED, 'Отменен')
)
name = models.CharField(max_length=32, verbose_name='Имя', blank=True)
phone_number = models.CharField(max_length=13, verbose_name='Номер телефона')
inst = models.CharField(max_length=64)
address = models.CharField(max_length=64, verbose_name='Адресс')
deliver_time = models.CharField(max_length=64, null=True, verbose_name='Время доставки')
comment = models.TextField(verbose_name='Комментарий')
status = models.CharField(max_length=32, choices=choises, null=True, verbose_name='Статус заказа')
created = models.DateTimeField(verbose_name='Время заказа', editable=False, default=datetime.now()+timedelta(hours=3))
def __str__(self):
return f'{self.name}'
def save(self, *args, **kwargs):
self.created = datetime.now() +timedelta(hours=3)
if self.id:
items = self.ordered_item.all()
for item in items:
item.save()
super(OrderSuit, self).save(*args, **kwargs)
class Meta:
verbose_name = 'Заказ костюма'
verbose_name_plural = 'Заказы костюмов'
If these are foreignKey fields, use raw_id_fields so you can search them in another window and it will make the load faster.
Photo1
need to use autocomplete_fields and register a new model for example SuitSearch, which has a search_fields which refers to model__name
admin.py
class SuitSearch(admin.ModelAdmin):
search_fields = ('model__name',)
class Meta:
model = SuitItem
admin.site.register(SuitItem, SuitSearch)
class SuitItemInOderInline(admin.TabularInline):
model = SuitInOder
autocomplete_fields = ('item',)
extra = 0
class SuitOrderAdmin(admin.ModelAdmin):
inlines = [SuitItemInOderInline]
list_display = ('inst', 'created')
list_filter = ('status',)
class Meta:
model = OrderSuit
admin.site.register(OrderSuit, SuitOrderAdmin)

Django admin: Show a field of a model depending on the value of another field

I have a model in my Django project called Category.
models.py
class Category(models.Model):
parent_category=models.ForeignKey('self', related_name='categories', on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, unique= True)
image = models.ImageField(upload_to='categories/', blank=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
Now in admin, I want to show the image field only if the parent field is not empty, on the fly. The parent field is a dropdown. I have a tried this, but it does not work:
admin.py
#admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
def get_fields(self, request, obj=None):
fields = super(CategoryAdmin, self).get_fields(request, obj)
for field in fields:
if field == 'parent_category' and 'parent_category'== None:
fields.remove('image')
return fields
prepopulated_fields = {'slug' : ('name',)}
I also tried to use jQuery.
admin.py
class Media:
js = (
'js/myscript.js',
)
myscript.js
if (!$) {
$ = django.jQuery;
}
(function($) {
$(document).ready(function() {
if ($('#parent_category').val() != "")
{
$('#image').hide()
}
else {
$('#image').show()
}
})
})(django.jQuery);
It shows the image field regardless of the value of parent_category. Any suggestions?
Thanks
Use a callable instead of a field. For example, I have a model which represents distance
#admin.register(Distance)
class DistanceAdmin(admin.ModelAdmin):
"""
Distance model admin
"""
list_display = (
'id',
'name',
'get_distance_in_km',
'get_distance_in_miles',
)
readonly_fields = (
'get_distance_in_km',
'get_distance_in_miles',
)
fields = (
'name',
'get_distance_in_km',
'get_distance_in_miles',
)
def get_distance_in_km(self, obj):
"""
Return the distance in kilometres, nicely formatted
"""
return obj.formatted_distance_in_km
get_distance_in_km.short_description = _('Distance in km')
get_distance_in_km.admin_order_field = 'distance_in_km'
def get_distance_in_miles(self, obj):
"""
Return the distance in miles, nicely formatted
"""
return obj.formatted_distance_in_miles
get_distance_in_miles.short_description = _('Distance in miles')
get_distance_in_miles.admin_order_field = 'distance_in_miles'
In these get_ methods of the admin class, you have access to the instance. So you might have a get_image method;
def get_image(self, obj):
if obj.parent_category:
return obj.image

Access a field of a through Model of an ManyToManyField before saving

I am trying to sum the cost of the ingredients of a recipe to have the total cost of a recipe. However when I try to access the cost of "ingredients" or "Ingredient.objects.filter(recipe=self)" these objects aren't created even after a manual save to super. I did however notice if I save it through the Django Admin twice the "Ingredient" objects are populated and the cost field is available. So how can I sum up the total cost of recipe cause my way seems not to work properly? Thanks in advance.
class Material(models.Model):
UNITS = [
('kg', 'kg'),
('g', 'gramm'),
('l', 'liter'),
('stk', 'Stuck'),
('dkg', 'deka'),
]
name = models.CharField(max_length=200)
unit = models.CharField(max_length=20, choices=UNITS, default="kg", verbose_name="Einheit")
lieferant = models.ForeignKey(Lieferant,default=1, verbose_name="Lieferant",on_delete=models.CASCADE, null=True)
costpunit = models.FloatField(verbose_name="Stuckpreis")
menge = models.FloatField(default=1,verbose_name="Menge")
costpkg = models.FloatField(editable=False, verbose_name="Kilopreis")
class Meta:
verbose_name_plural = "Materialien"
def __str__(self):
return self.name
def save(self):
if self.unit and (self.unit is not "stk"):
kilogramms = convert_SI(self.menge, self.unit, "kg")
self.costpkg = self.costpunit/kilogramms
super(Material, self).save()
class Recipe(models.Model):
name = models.CharField(max_length=200)
cost = models.FloatField(default=1, editable=False, verbose_name="Kosten")
ingredients = models.ManyToManyField(Material, through='Ingredient')
def __str__(self):
return self.name
def save(self):
x = 0
super(Recipe, self).save()
for ing in Ingredient.objects.filter(recipe=self):
x += ing.cost
self.cost = x
super(Recipe, self).save()
class Ingredient(models.Model):
UNITS = [
('kg', 'kg'),
('g', 'gramm'),
('l', 'liter'),
('stk', 'Stuck'),
('dkg', 'deka'),
]
material = models.ForeignKey(Material, default=1, verbose_name="Material", on_delete=models.CASCADE, null=True)
recipe = models.ForeignKey(Recipe, default=1, verbose_name="Recipe", on_delete=models.CASCADE, null=True)
menge = models.FloatField(verbose_name="Menge")
unit = models.CharField(max_length=20, choices=UNITS, default="kilo", verbose_name="Einheit")
cost = models.FloatField(default=1, editable=False, verbose_name="Kosten")
def __str__(self):
return self.recipe.name + ": " + self.material.name
def save(self):
if self.unit and (self.unit is not "stk"):
kilogramms = convert_SI(self.menge, self.unit, "kg")
self.cost = kilogramms*self.material.costpkg
super(Ingredient, self).save()
After some further debugging I found the actual problem it was the way django saves the recipe in admin it first saves the recipe and then the ingredients so the solution was to edit the related_save method of admin.ModelAdmin for recipe and it looks something like this:
class RecipeAdmin(admin.ModelAdmin):
inlines = (IngredientInlineAdmin,)
fields = ('name', 'cost')
readonly_fields = ["cost"]
def save_related(self, request, form, formsets, change):
super(RecipeAdmin, self).save_related(request, form, formsets, change)
# get the recipe from the form
recip = form.instance
recip.cost = 0
# iterate through the saved ingredients
for ingredient in Ingredient.objects.filter(recipe=recip):
# populate the cost field of the recipe
recip.cost += ingredient.cost
print(recip.cost)
# and save the recipe again after the ingredients were saved
recip.save()

ModelChoiceField initial value from db

I tried to find out how to set the initial value of a ModelChoiceField, I found many answers to this question but I don't really get them. I understand that I can set "initial" when calling the form in admin.py but then a model instance is mentioned and I am lost.
This is my models.py
class Articles(models.Model):
headline = models.CharField('Rubrik', max_length=200)
category = models.CharField('Kategori', max_length=200, blank=True)
extract = models.TextField('Utdrag')
image = ImageField('Bild', upload_to='articles', blank=True, default="")
text = RichTextUploadingField('Text', blank=True, default="")
added = models.DateTimeField('Publicerad', default=timezone.now, blank=True)
updated = models.DateTimeField('Uppdaterad',auto_now=True)
frontpage = models.BooleanField('Visa på startsida', default=True)
active = models.BooleanField('Aktiv', default=False)
def save(self, *args, **kwargs):
if self.added is None:
self.added = timezone.now
super(Articles, self).save(*args, **kwargs)
def __unicode__(self):
return '%s' % (self.headline)
class Meta:
verbose_name = "artikel"
verbose_name_plural = "Artiklar"
This is my forms.py
class ArticleForm(forms.ModelForm):
category = forms.ModelChoiceField(queryset=Menu.objects.order_by('name').filter(category=True))
This is my admin.py
class ArticlesAdmin(admin.ModelAdmin):
form = ArticleForm
list_display = ('headline','category', 'extract', 'image', 'added', 'updated', 'frontpage', 'active')
admin.site.register(Articles, ArticlesAdmin)
When I edit the article in the admin section I want the stored value of the category to be the initial value for the ModelChoiceField. Do you get what I mean?
In admin.py there should be something like:
form = ArticleForm(initial = {'category': instance.something})
*EDIT: I added ForeignKey as suggested
category = models.ForeignKey(Menu)
and admin.py looks like this:
class ArticlesAdmin(admin.ModelAdmin):
form = ArticleForm
list_display = ('headline','category', 'extract', 'image', 'added', 'updated', 'frontpage', 'active')
And now it's working as expected!
This code should work:
form = ArticleForm(initial = {'category': pk})
pk is the stored value, pk = primary key

Categories

Resources