I created a ProductAttributes model that have a ForeignKey from Product model
now i'm trying to create an admin panel for adding product using django admin
i'm adding ProductAttributes to Product admin with TabularInline but its not working
this the models and admin classes
#models
class Product(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField()
description = models.TextField(null=True, blank=True)
introduction = models.TextField(null=True, blank=True)
unit_price = models.DecimalField(
max_digits=12,
decimal_places=2,
validators=[MinValueValidator(1)])
inventory = models.IntegerField(validators=[MinValueValidator(0)])
last_update = models.DateTimeField(auto_now=True)
collection = models.ForeignKey(Collection, on_delete=models.PROTECT, related_name='products')
promotions = models.ManyToManyField(Promotion, blank=True)
def __str__(self) -> str:
return self.title
class Meta:
ordering = ['title']
class ProductAttributes(models.Model):
Product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="attributes")
attribute = models.CharField(max_length=255)
#admin
class ProductAttributesInline(admin.TabularInline):
model = models.ProductAttributes
#admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin):
autocomplete_fields = ['collection']
prepopulated_fields = {
'slug': ['title']
}
actions = ['clear_inventory']
inlines = [ProductAttributesInline]
list_display = ['title', 'unit_price',
'inventory_status', 'collection_title']
list_editable = ['unit_price']
list_filter = ['collection', 'last_update', InventoryFilter]
list_per_page = 10
list_select_related = ['collection']
search_fields = ['title']
def collection_title(self, product):
return product.collection.title
#admin.display(ordering='inventory')
def inventory_status(self, product):
if product.inventory < 10:
return 'Low'
return 'OK'
#admin.action(description='Clear inventory')
def clear_inventory(self, request, queryset):
updated_count = queryset.update(inventory=0)
self.message_user(
request,
f'{updated_count} products were successfully updated.',
messages.ERROR
)
class Media:
css = {
'all': ['store/style.css']
}
the ProductAttributes isnt shown in Product admin
in the orginal project i created another inline for ProductImage and its working but when i try to delete that inline its not gone from product admin
Firstly, do not forget checking all migrations, and It would be more good to keep your models in models.py and do not mix them with admin related changes. I would recommend you to write them in admin.py.
You can use both images and attributes like that:
class ProductAttributesInlineAdmin(admin.TabularInline):
model = ProductAttributes
extra = 2
#admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin):
...
inlines = [ProductAttributesInlineAdmin, ProductImageInlineAdmin]
Related
I'm trying to make a Django model, with Django Rest Framework. I want this to allow me to load one or more images in the same input.
MODELS:
from django.db import models
from datetime import datetime
from apps.category.models import Category
from django.conf import settings
class Product(models.Model):
code = models.CharField(max_length=255, null=True)
name = models.CharField(max_length=255)
image = models.ImageField(upload_to='photos/%Y/%m/', blank = True, null=True, default='')
description = models.TextField()
caracteristicas = models.JSONField(default=dict)
price = models.DecimalField(max_digits=6, decimal_places=2)
compare_price = models.DecimalField(max_digits=6, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
quantity = models.IntegerField(default=0)
sold = models.IntegerField(default=0)
date_created = models.DateTimeField(default=datetime.now)
def __str__(self):
return self.name
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name = 'images')
image = models.ImageField(upload_to='photos/%Y/%m/', default="", null=True, blank=True)
SERIALIZER:
from rest_framework import serializers
from .models import Product, ProductImage
class ProductImageSerializer(serializers.ModelSerializer):
class Meta:
model = ProductImage
fields = ["id", "product", "image"]
class ProductSerializer(serializers.ModelSerializer):
images = ProductImageSerializer(many=True, read_only=True)
uploaded_images = serializers.ListField(
child = serializers.ImageField(max_length = 1000000, allow_empty_file = False, use_url = False),
write_only=True
)
class Meta:
model = Product
fields = [
'id',
'code',
'name',
'description',
'caracteristicas',
'price',
'compare_price',
'category',
'quantity',
'sold',
'date_created',
'images',
'uploaded_images'
]
def create(self, validated_data):
uploaded_images = validated_data.pop("uploaded_images")
product = Product.objects.create(**validated_data)
for image in uploaded_images:
newproduct_image = ProductImage.objects.create(product=product, image=image)
return product
I would simply like how to make the following input field allow me to load more than one image:
Imagen de referencia input
thank you very much
You didn't post your admin.py but my guess is that you also need to register your ProductImage model as an inlines since you already use a One2Many relationship between Product and ProductImage:
In your admin.py:
class ProductImageAdmin(admin.StackedInline):
model = ProductImage
class ProductAdmin(admin.ModelAdmin):
inlines = [ProductImageAdmin]
class Meta:
model = Product
admin.site.register(ProductImage)
admin.site.register(Product, ProductAdmin)
You can also check this SO answer out for more details.
Hope that helps :)
serializers.py
class RegSerializer(serializers.ModelSerializer):
admin = serializers.SlugRelatedField(slug_field='username', read_only=True)
class Meta:
model = Registration
fields = [
'id', 'rooms', 'first_name', 'last_name','admin', 'pasport_serial_num', 'birth_date', 'img', 'visit_date',
'leave_date', 'guest_count', 'room_bool']
models.py
class Rooms(models.Model):
objects = None
room_num = models.IntegerField(verbose_name='Комната')
room_bool = models.BooleanField(default=True, verbose_name='Релевантность')
category = models.CharField(max_length=150, verbose_name='Категория')
price = models.IntegerField(verbose_name='Цена (сум)', null=True)
def __str__(self):
return f'{self.room_num}'
class Meta:
verbose_name = 'Комнату'
verbose_name_plural = 'Комнаты'
class Registration(models.Model):
objects = None
rooms = models.ForeignKey(Rooms, on_delete=models.CASCADE, verbose_name='Номер',
help_text='Номер в который хотите заселить гостя!',
)
first_name = models.CharField(max_length=150, verbose_name='Имя')
last_name = models.CharField(max_length=150, verbose_name='Фамилия')
admin = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='Администратор')
pasport_serial_num = models.CharField(max_length=100, verbose_name='Серия паспорта', help_text='*AB-0123456')
birth_date = models.DateField(verbose_name='Дата рождения')
img = models.FileField(verbose_name='Фото документа', help_text='Загружайте файл в формате .pdf')
visit_date = models.DateField(
default=django.utils.timezone.localdate, verbose_name='Дата прибытия')
leave_date = models.DateField(blank=True, null=True, verbose_name='Дата отбытия', default='После ухода!')
guest_count = models.IntegerField(default=1, verbose_name='Кол-во людей')
room_bool = models.BooleanField(default=False, verbose_name='Релевантность',
help_text='При бронирование отключите галочку')
price = models.IntegerField(verbose_name='Цена (сум)', null=True)
def __str__(self):
return f'{self.rooms},{self.last_name},{self.first_name},{self.room_bool}'
class Meta:
verbose_name = 'Номер'
verbose_name_plural = 'Регистрация'
how can I make it so that the name of the user who registered room is indicated in the admin field and without the right to change only readonly?
can this be done at all?
thanks in advance for your reply
You can pass additional attributes to serilizer's save method. In your view, you can call serializer save() with admin argument like this:
def your_view(request):
# your code
serializer.save(admin=request.user)
Or if you want to do it on admin page, you can override your admin's save_model method. Also you should specify admin as a readonly:
class RegistrationAdmin(admin.ModelAdmin):
readonly_fields = ('admin',)
def save_model(self, request, obj, form, change):
if not obj.pk:
# Only set admin during the first save.
obj.admin = request.user
super().save_model(request, obj, form, change)
I set serializers.HiddenField and by default set CurrentUserDefault() from serializers
automatically substitutes the value of the current admin and at the same time the admin field now goes to the api
class RegSerializer(serializers.ModelSerializer):
admin = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Registration
exclude = ['price', 'visit_date']
I have a model Product with a User and ProductImages as foreign key.
models.py
class User(AbstractBaseUser):
...
class ProductImages(models.Model):
image_type = models.CharField(max_length=33,default='image_type')
image_file = models.ImageField(
upload_to='images/',
null=True,
blank=True,
default='magickhat-profile.jpg'
)
class Product(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
productimages = models.ForeignKey(ProductImages, on_delete=models.CASCADE)
product_title = models.CharField(max_length=255, default='product_title')
product_description = models.CharField(max_length=255, default='product_description')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
product_view = models.IntegerField(default=0)
def __str__(self):
return self.product_title
def get_absolute_url(self):
return reverse('product_change', kwargs={'pk': self.pk})
forms.py
class ProductForm(ModelForm):
productimages = forms.ImageField()
CHOICES_STATUS = (('Pronto', 'Pronto'),('Em obras', 'Em obras'),('Lançamento', 'Lançamento'),)
product_status = forms.ChoiceField(choices=CHOICES_STATUS)
product_title = forms.CharField()
product_description = forms.CharField()
class Meta:
model = Product
fields = '__all__'
admin.py
class AdminProductModel(admin.ModelAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(user_id=request.user)
kadmin.register(User, UserAdmin)
kadmin.register(Product, AdminProductModel)
But in the admin Product model the field for image is redering as select field no ImageField
My purpose is add a upload image field on django model administration.
If i use ImageField direct on Product model the field is redering ok, but i need a external table to store that image becouse is a multimage upload, so i need a foreing key.
How is the right way to that purpose.
I see other questions about that, but the majority is old versions, and is not for custom AdminSite, like me.
Django 3.2 version Class View Based
As suggested in comments i change the relationship between models like:
uptade
models.py
class Product(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
product_title = models.CharField(max_length=255, default='product_title')
product_description = models.CharField(max_length=255, default='product_description')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
product_view = models.IntegerField(default=0)
def __str__(self):
return self.product_title
def get_absolute_url(self):
return reverse('product_change', kwargs={'pk': self.pk})
class ProductImages(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
image_type = models.CharField(max_length=33,default='image_type')
image_file = models.ImageField(
upload_to='images/',
null=True,
blank=True,
default='magickhat-profile.jpg'
)
forms.py
class ProductForm(ModelForm):
image_file = forms.ImageField()
CHOICES_STATUS = (('Pronto', 'Pronto'),('Em obras', 'Em obras'),('Lançamento', 'Lançamento'),)
product_status = forms.ChoiceField(choices=CHOICES_STATUS)
product_title = forms.CharField()
product_description = forms.CharField()
class Meta:
model = Product
fields = '__all__'
My question now is, how bring that field image_file from ProducImages to ProductForm?
Am guessing you want to be able to post multiple images to your database in that case you should use StackedInline to merge the two models together then you can have multiple fields in the database to upload more images. You do that through the admin.py like so:
class ProductImagesAdmin(admin.StackedInline):
model = ProductImages
class ProductAdmin(admin.ModelAdmin):
# The field your want to display
list_display = ['','']
# Here you link the models together
inlines = [ ProductImagesAdmin]
To upload multiple images from the frontend you will need a form to handle
class ProductImagesForm(forms.ModelForm):
class Meta:
model = ProductImages
fields = ['image_file']
widget = forms.ClearableFileInput(attrs={'multiple':True})
View to handle multiple images
def upload_product_images(request):
form = ProductImagesForm(request.POST, request.FILES)
files = requests.FILES.getlist['image_file']
title = request.POST['product_title']
description = request.POST['descritpion']
product = Product(product_title=title,product_description=description)
product.save()
# You would have to make some iterations through the list images in order topost
# them to your database
if form.is_valid:
for file in files:
prod_image_data = ProductImages(product=product, image=file)
prod_image_data.save()
return redirect(# redirect to anywhere you)
I have 2 models, Product and Tag. The relation between product and tag is Many-to-Many Relationship.
How to show "tags" field on django admin panel? Currently the value is None when I am using the code below
models.py
class Tag(models.Model):
name = models.CharField(max_length=200, null=True)
def __str__(self):
return self.name
class Product(models.Model):
CATEGORY = (
('Indoor','Indoor'),
('Outdoor','Outdoor'),
)
name = models.CharField(max_length=200, null=True)
price = models.FloatField(null=True)
category = models.CharField(max_length=200, choices=CATEGORY)
description = models.CharField(max_length=200, null=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
tags = models.ManyToManyField(Tag)
admin.py
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['id','name','price','category','tags']
list_display_links = ['name']
def tags(self):
return self.tags.name
try this
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['id', 'name', 'price', 'category', 'get_tags']
list_display_links = ['name']
def get_tags(self, obj):
if obj.tags.all():
return list(obj.tags.all().values_list('name', flat=True))
else:
return 'NA'
Refer this https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['id','name','price','category','get_tags']
list_display_links = ['name']
def get_tags(self, instance):
return [tag.name for tag in instance.tags.all()]
In Django 4.1 you don't need to pass the second param (instance of class), and can do smth like this:
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['id','name','price','category','get_tags']
list_display_links = ['name']
def get_tags(self):
return [tag.name for tag in instance.tags.all()]
Also, you can detalize this field, and add decorator #admin.display example for add title for django admin. More details in docs: https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display
this is my setting for translations in the models.py file, django-parler 2.0.1 won't show fields for Products in the admin site after I had synch migrations. I am currently using Django 3.0.3.
from django.db import models
from django.urls import reverse
from parler.models import TranslatableModel, TranslatedFields
class Category(TranslatableModel):
translations = TranslatedFields(
name = models.CharField(max_length=200,
db_index=True),
slug = models.SlugField(max_length=200,
db_index=True,
unique=True)
)
class Meta:
# ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('shop:product_list_by_category',
args=[self.slug])
class Product(TranslatableModel):
translations = TranslatedFields(
name = models.CharField(max_length=200, db_index=True),
slug = models.SlugField(max_length=200, db_index=True),
description = models.TextField(blank=True)
)
category = models.ForeignKey(Category,
related_name='products',
on_delete=models.CASCADE)
image = models.ImageField(upload_to='products/%Y/%m/%d',
blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
#class Meta:
# ordering = ('name',)
# index_together = (('id', 'slug'),)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('shop:product_detail',
args=[self.id, self.slug])
I have registered the model in the admin.py file but it won't show the fields for product description and price all I get is the translated tab.
from django.contrib import admin
from .models import Category, Product
from parler.admin import TranslatableAdmin
#admin.register(Category)
class CategoryAdmin(TranslatableAdmin):
list_display = ['name', 'slug']
def get_prepopulated_fields(self, request, obj=None):
return {'slug': ('name',)}
#admin.register(Product)
class ProductAdmin(TranslatableAdmin):
list_display = ['name', 'slug', 'price',
'available', 'created', 'updated']
list_filter = ['available', 'created', 'updated']
list_editable = ['price', 'available']
def get_prepopulated_fields(self, request, obj=None):
return {'slug': ('name',)}
I wonder what am doing wrong that I am getting this and I wonder if there's a better way to make translate configurations with Django-parler 2.0.1. any suggestions is welcomed!!
Apparently, I was able to fix the problem by first deleting all migrations and I also had to trash my database and switch to PostgreSQL and after that, I reapplied migrations and it worked. All fields are now visible in the admin site now yippee! but be mindful to make a backup of your database because everything will be deleted..