Django Admin upload image with foreign key - python

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)

Related

How to get specific objects based on ManyToMany field match

I'm doing a cookbook app, which help users find meal thay can do with their ingridients. I'm using Django RestFramework, and i need to return list of avaliable meals that user can do, but don't know how to do search by ingridients
My models.py:
#models.py
class Meal(models.Model):
name = models.CharField(max_length=250)
description = models.TextField(blank=True, null=True)
recipe = models.TextField()
is_published = models.BooleanField(default=False)
category = ForeignKey('Category', on_delete=models.CASCADE, null=True)
user = ForeignKey(User, verbose_name='User', on_delete= models.CASCADE)
difficulty = ForeignKey('Difficulty', on_delete=models.PROTECT, null=True)
ingridients = models.ManyToManyField('Ingridient')
class Ingridient(models.Model):
name = models.CharField(max_length=100, db_index=True)
ico = models.ImageField(upload_to="photos/%Y/%m/%d/", blank=True, null=True)
category = ForeignKey('CategoryIngridients', on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
class CookBookUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
ingridients = models.ManyToManyField('Ingridient')
serializer.py
class MealSerializer(serializers.ModelSerializer):
class Meta:
model = Meal
fields = "__all__"
views.py
class CraftWithUsersIngridientsListAPIView(generics.ListAPIView):
serializer_class = MealSerializer
def get_queryset(self):
return Meal.objects.filter(ingridients=CookBookUser.objects.filter(user_id = self.request.user.id).ingridients)
CraftWithUsersIngridientsListAPIView isn't working and I get AttributeError 'QuerySet' object has no attribute 'ingridients', can someone help fix this?
I tried building different serializer but it doesn't help
class CraftWithUsersIngridientsListAPIView(generics.ListAPIView):
serializer_class = MealSerializer
def get_queryset(self):
user_ingredients = CookBookUser.objects.get(user=self.request.user).ingredients.all()
return Meal.objects.filter(ingredients__in=user_ingredients)
This way, you first get the CookBookUser instance for the current user, then get all of their ingredients, and finally, filter the Meal objects that contain those ingredients. The __in query lookup is used to check if the meal ingredients are in the user's ingredients.

an Inline class isnt working in django admin

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]

Accessing other model field when validating model field in Django

So I have a Django auctions app, which has 4 models: Users, Listings, Bids, Comments.
When a user tries to place a bid on some listing, I want to check whether bid_amount field in Bid model is smaller than start starting_bid field in Listing model. Also, I wanted to ask, what is the best practice for this kinda stuff? AFAIK, you can validate a form field in forms.py. Thanks!
models.py
class Listing(models.Model):
"""Auction listing"""
user = models.ForeignKey(User, verbose_name='user owner', on_delete=models.CASCADE, related_name="usr_listings")
title = models.CharField(max_length=64)
description = models.TextField(max_length=160)
starting_bid = models.PositiveIntegerField()
bids_number = models.PositiveIntegerField(default=1)
img_url = models.URLField("Image URL", max_length=200, blank=True)
category = models.CharField(max_length=64, blank=True)
date_listed = models.DateTimeField(default=timezone.now)
class Meta:
verbose_name = 'auction listing'
ordering = ['-date_listed']
def __str__(self):
return self.title
def get_absolute_url(self):
print('loading... get_absolute_url')
return reverse('listing_detail', kwargs={'pk': self.pk})
class Bid(models.Model):
"""Bids made on auction listing"""
listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="listing_bids")
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_bidder")
bid_amount = models.PositiveIntegerField(default=None)
class Meta:
verbose_name = 'Bid'
def __str__(self):
return f'{self.bid_amount} by {self.user} on {self.listing}'
def clean(self):
super().clean()
# do something to access Listing.starting bid, and Listing.user
adding this to the clean method of Bid solve the problem for you.
from django.core.exceptions import ValidationError
...
if self.bid_amount < self.listing.starting_bid:
raise ValidationError("bid_amount must be greater than or equal to starting_bid of the listing")

AttributeError; Dynamic url for list of charts by category issue (queryset filter)

Django 'CategorisedListView' object has no attribute 'slug' or Page not found (error 404) issue
I'm on Django 2.0, using generic class list view. I have tried dynamic url based on slug and queryset filter on slug to get list of charts by category.
Please help! I've been stuck here for a couple of days since.
views.py
class CategoriesView(generic.ListView):
template_name = 'Bokeh/categories.html'
context_object_name = 'all_categories'
def get_queryset(self):
return Category.objects.all()
class CategorisedListView(generic.ListView):
model = Category
template_name = 'categories/list_of_charts_by_category.html'
context_object_name = 'categorised'
def get_queryset(self):
self.category = get_object_or_404(Category, name = self.kwargs['slug'])
return Chart.objects.filter(category=self.slug)
models.py
class Category(models.Model):
name = models.CharField(max_length=100)
image_file = models.ImageField(default=None, unique=True)
slug = models.SlugField(max_length=100, unique=True)
parent = models.ForeignKey('self', on_delete=models.PROTECT, blank=True, null=True, related_name='children')
def __str__(self):
return self.name
def get_absolute_url(self):
return '{slug}/'.format(slug=self.slug)
class Meta:
ordering = ('name',)
verbose_name = 'Category'
verbose_name_plural = 'Categories'
class Chart(models.Model):
name = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique=True)
description = models.TextField(max_length=250)
url = models.URLField(default=None, blank=True)
embed_url = models.TextField(default=None, blank=True)
image_file = models.ImageField(default=None, unique=True)
code_file = models.FileField(default=None, blank=True, unique=True)
chart_library = models.CharField(max_length=250)
author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
tag = TaggableManager()
category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True)
def __str__(self):
return self.name + ' - ' + self.chart_library
def get_absolute_url(self):
return reverse('Bokeh:detail', kwargs={'pk': self.pk})
def read_file(self):
data = self.code_file.path
with open(self.code_file.path, 'r', encoding='UTF-8') as data:
content = data.read()
return content
class Meta:
ordering = ('name',)
urls.py
path("categories/", views.CategoriesView.as_view(), name='categories'),
# /Bokeh/categories/<category_slug>
path("categories/<slug:slug>/", views.CategorisedListView.as_view(), name='list_of_charts_by_category'),
it is supposed to query the database when a specific category is clicked and return the list of charts categorised under that specific category. However, the page throws 'CategorisedListView' object has no attribute 'slug'
The lookup you're doing in get_queryset() isn't quite right. Firstly, you seem to be looking up the Category by name rather than by the slug. Then you try and filter the Charts using their category attribute, but instead of passing the category you pass a non-existent attribute self.slug.
Try changing the implementation to this:
def get_queryset(self):
# Lookup the Category using it's slug attribute, not name, and assign to local variable
category = get_object_or_404(Category, slug=self.kwargs['slug'])
# Now lookup the Charts using the category we just looked up
return Chart.objects.filter(category=category)

Django ModelForm ForeignKey query

I want to have a form which only offers the user to post a question for a project he is participating in.
models.py:
class Project(models.Model):
project_name = models.CharField(max_length=255, unique=True, blank=False)
def __str__(self):
return str(self.project_name)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
project = models.ManyToManyField(Project)
def __str__(self):
return str(self.user)
class Question(models.Model):
title = models.CharField(max_length=255, blank=False)
content = tinymce_models.HTMLField(blank=False)
author = models.ForeignKey(User, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
...
def __str__(self):
return str(self.title)
class QuestionForm(ModelForm):
class Meta:
model = Question
fields = ['title', 'content', 'project']
in views.py:
form = QuestionForm()
form.fields["project"].queryset = Project.objects.filter(project_name__in=request.user.profile.project.all())
But somehow the result of the query always stays empty.
Does somebody maybe have an idea what I am missing?
Your query is over complicated. You should just use the user's projects directly:
form.fields["project"].queryset = request.user.profile.project.all())

Categories