Good day, not so long ago, working with Django.
Encountered the following problem.
There are two models
class Product(models.Model):
productID = models.AutoField(primary_key=True)
brandID = models.ForeignKey(Brand, verbose_name=u'Бренд')
categoryID = models.ForeignKey(Category, verbose_name=u'Категория')
productPhotos = models.ManyToManyField("ProductPhoto", verbose_name = "Фотографии товара", related_name="photos", blank=True)
class ProductPhoto(models.Model):
productPhotoID = models.AutoField(primary_key=True)
productID = models.ForeignKey(Product)
imageFile = models.FileField(u'Путь к изображение', upload_to=makeUploadPath)
dateModified = models.DateField(auto_now=True)
For admin use the following TabularInline
class ProductPhotoInline(admin.TabularInline):
model = ProductPhoto
verbose_name = u"Фото"
verbose_name_plural = u"Фотографии"
class ProductAdmin(admin.ModelAdmin):
list_display = ('title')
search_fields = ...
fieldsets = ...
)
inlines = [ ProductPhotoInline, ]
The problem when you add a product in the table ProdcutPhoto productId field is filled, and the field in the table Products productPhotos is empty. How best to do to fill both fields.
You should remove the productPhotos ManyToMany field, it's redundant. If each photo is only attached to one product, then the productID ForeignKey is all you need.
To access the photos from a product instance, you can use productphoto_set, e.g.
product = Product.objects.get(pk=1)
photos = product.productphoto_set.all()
Related
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]
I have such structure:
#admin.py
class OrderProductInline(admin.TabularInline):
model = OrderProduct
extra = 0
template = "admin/edit_inline/tabular_order.html"
#admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
...
inlines = [
OrderProductInline
]
#models.py
class OrderProduct(models.Model):
...
order_id = models.ForeignKey(Order, on_delete=models.CASCADE, blank=False,
null=False)
to_product = models.ForeignKey(Product, on_delete=models.SET_NULL, blank=False,
null=True, verbose_name=_('Product'))
On the attached image I show some custom inputs (red rectangles) for each product item in inline form. This inputs depends on additional queryset based on Foreign Key, but I cant`t find the correct way how to display this fields there.
I want to use uuid field as my id (primary key) but there is something wrong with it and i can't fix it
this is my model
class Cart(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
class CartItem(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE , related_name='items')
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField()
class Meta:
unique_together = [['cart'], ['product']]
This Is MY Serializer.py
class CartItemSerializer(serializers.ModelSerializer):
class Meta:
model = Cart
fields = ['id', 'product', 'quantity']
class CartSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(read_only=True)
items = CartItemSerializer(many=True)
class Meta:
model = Cart
fields = ['id', 'items']
And My Views.py is
class CartViewSet(CreateModelMixin, RetrieveModelMixin, GenericViewSet):
queryset = Cart.objects.prefetch_related('items__product').all()
serializer_class = CartSerializer
My database Is postgres Sql
My Error when I browse my api my guid
Make sure that you create a UUID field uuid = UUIDField(primary_key=True, default=None, editable=False) first before your initial migration.
If you have already done the initial migration, then drop the database and recreate it and don't forget to delete the migration files.
Run migrations again and you should be good to go.
I have 2 different registers on my admin panel and on one TabularInline allows multiple choice:
and on another not:
Where does the difference come from? How can I control it?
Update:
I have a table movies which is connected to the table person via intermediate table. Table Person has name and role (actor, writer, director). What I am trying to do is to allow in admin panel to see and edit both, e.g. Movies should have 3 different panels, where one can add actor, writer and director. Actor should have name and movie to add.
So far I was able to make 3 panels for movies, but they do not allow several choices and show all names, but not only actors in actors panel (see second picture). Also when I save them, they do not get role - actor. Actors allow to choose several movies, but when I save them, admin blocks me saying "Film work person with this Movie already exists."
My models (I only show part for films and actors, writers and director are the same):
class FilmWorkPerson(models.Model):
id = models.AutoField(primary_key=True, editable=False)
movie = models.OneToOneField('FilmWork', models.CASCADE, unique=False)
person = models.ForeignKey('Person', models.CASCADE, unique=False)
created_on = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'film_work_person'
unique_together = (('movie', 'person'),)
class FilmWork(models.Model):
# what we have from sql
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255)
people = models.ManyToManyField('Person', through=FilmWorkPerson)
class Person(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(_('ФИО'), max_length=100)
role = models.CharField(_('роль'), max_length=100)
created_on = models.DateTimeField(auto_now =True)
movie = models.ManyToManyField(FilmWork, through=FilmWorkPerson)
class Meta:
db_table = 'person'
def __str__(self):
return self.name
class ActorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(role='actor')
class Actor(Person):
objects = ActorManager()
class Meta:
proxy = True
verbose_name = _('actor')
verbose_name_plural = _('actors')
my admin.py:
class PersonInlineAdmin(admin.TabularInline):
model = Person.movie.through
class ActorInlineFormSet(BaseInlineFormSet):
def get_queryset(self):
qs = super(ActorInlineFormSet, self).get_queryset().filter(person__role__iexact='actor')
self._queryset = qs
return self._queryset
class ActorInlineAdmin2(admin.TabularInline):
model = FilmWork.people.through
formset = ActorInlineFormSet
verbose_name = 'actor'
verbose_name_plural = 'actors'
#admin.register(FilmWork)
class FilmworkAdmin(admin.ModelAdmin):
# отображение полей в списке
list_display = ('title', 'ratings', 'created_on', 'actors', 'writers', 'director',
'film_creation_date', 'age_limit', 'link')
def actors(self, obj):
actors = FilmWork.objects.filter(person__role__iexact='actor', title=obj).values_list('person__name', flat=True)
return ", ".join([a for a in actors])
def writers(self, obj):
writers = FilmWork.objects.filter(people__role__iexact='writer', title=obj).values_list('people__name', flat=True)
return ", ".join([a for a in writers])
def director(self, obj):
director = FilmWork.objects.filter(people__role__iexact='director', title=obj).values_list('people__name', flat=True)
return ", ".join([a for a in director])
list_filter = ('genre','film_creation_date', 'age_limit', 'link')
fields = ('title', 'plot', 'ratings', 'film_creation_date', 'age_limit', 'link')
search_fields = ('title', 'plot', 'id', 'film_creation_date', 'age_limit', 'link')
inlines = (GenreInlineAdmin, ActorInlineAdmin2, DirectorInlineAdmin2, WriterInlineAdmin2, )
#admin.register(Actor)
class Actor(admin.ModelAdmin):
list_display = ('name', )
fields = ('name', )
search_fields = ('name', 'movie')
list_filter = ('movie',)
inlines = (PersonInlineAdmin,)
I'm not sure if I understand your question clearly, but I'm guessing it's because of the different relationship types between the models, models.ForeignKey for the Genre vs models.ManyToManyField for the Movie.
If you can provide more details on what you're trying to achieve, I might be able to help.
I have designed following models for my blog
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.TextField(default='')
created_at = models.DateTimeField('created date', auto_now_add=True, auto_now=False)
updated_at = models.DateTimeField('updated date', auto_now_add=False, auto_now=True)
author = models.ForeignKey('Author', default='admin')
def __str__(self):
return self.title
class Author(models.Model):
name = models.CharField(max_length=150)
email = models.EmailField(blank=True)
bio = models.TextField()
def __str__(self):
return self.name
class Category(models.Model):
cat_name = models.CharField(max_length=200)
post = models.ManyToManyField('Post')
def __str__(self):
return self.cat_name
class Tag(models.Model):
tag_name = models.CharField(max_length=200)
post = models.ManyToManyField('Post')
def __str__(self):
return self.tag_name
and I am trying to register this model under django admin in such a way that. I can edit the Category, Tags and Authors from the Post page. but I am having hard time to accomplish this talk, I have written this code in admin.py file
from django.contrib import admin
from .models import Post, Author, Tag, Category
class AuthorInline(admin.TabularInline):
model= Author
class TagInline(admin.StackedInline):
model= Tag
class CategoryInline(admin.StackedInline):
model = Category
#admin.register(Post) #another method of registration admin.site.register(Post, PostAdmin)
class PostAdmin(admin.ModelAdmin):
#Show the following fields in this order
fields = ['body', 'title']
#show the following filelds for nice formattng
list_display = ['title', 'author', 'created_at']
#display based on the date hirerarchy
date_hierachy = 'created_at'
#embed the following child models in this parent models
inlines = [AuthorInline, TagInline, CategoryInline,]
#to exclude fields
exclude = ('author',)
When I run my server I got the errors like
ERRORS:
<class 'blogs.admin.AuthorInline'>: (admin.E202) 'blogs.Author' has no ForeignKey to 'blogs.Post'.
<class 'blogs.admin.CategoryInline'>: (admin.E202) 'blogs.Category' has no ForeignKey to 'blogs.Post'.
<class 'blogs.admin.TagInline'>: (admin.E202) 'blogs.Tag' has no ForeignKey to 'blogs.Post'.
when investigating the error, we cannot have StackedInline class if the models doesn't have foreign key, but How can I put the Tags, Category and Author rendered formm under the Post page in django admin,
For using AuthorInline, you ned a foreignkey field in you Author model
ex:
class Author(models.Model):
post = models.ForeignKey('Post')
This means one post may have multiple authors.
But here in your situation you have the correct model and fileds which have one author for one post, so you can remove AuthorInline.
And incase of Tag and Category, you are using many-to-many field, It will be good if you go through this documentation https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models
You have to rewrite the CategoryInline and TagInline;
class TagInline(admin.StackedInline):
model= Tag.post.through
class CategoryInline(admin.StackedInline):
model = Category.post.through
This isn't what inlines are for, and you don't want them here.
Inlines are for the reverse relation: given an author, edit their details and enter all their books on the same page. Your foreign keys and many-to-many fields are best shown as simple widgets, which is what Django does by default; the author and category will be displayed as a dropdown allowing you to choose an item, and the tags will be displayed as a multi-select box.
You might also choose to register Book as an inline on the Author admin; that's up to you.
Finally I made, what I wanted, the main gist is to make the category, author and tags choosable from the post page, so to do that, we need to add all the fields in the post model, which is the modified model
from django.db import models
from django.utils import timezone
class Author(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(blank=True)
bio = models.TextField()
class Tag(models.Model):
tag_name = models.CharField(max_length=50)
class Category(models.Model):
cat_name = models.CharField(max_length=50)
class Post(models.Model):
'''post can have many categories
and categories can have many post
author can have many post but post
can have single author
post can have many tags, and tags
can have many posts'''
title = models.CharField('post title', max_length=200)
body = models.TextField(default='', null=True)
created_at = models.DateTimeField(auto_now_add=True, auto_now=False)
updated_at = models.DateTimeField(auto_now_add=False, auto_now=True)
author = models.ForeignKey(Author, verbose_name = "List of Author") #many to one relationship
def __str__(self):
return self.title
#Generally many to many fields should into that model which is going to be edited.
tags = models.ManyToManyField(Tag)
categories = models.ManyToManyField(Category)
class Meta:
ordering = ['-created_at']
verbose_name_plural = "Posteeees"
# def post_status(self):
# return timezone.now() - self.updated_at <= 1
#Recursive realation, we can define the foreignkey itself to the model and this is called rrecursive realation
#