How to use API rest Framework for Images in Django - python

I am trying to create product model for my product details. I have some images for one product. I want to add multiple images in my django dashboard so I wrote this codes now I want show them in my template but I can't why?
models.py:
class Product(models.Model):
name =models.CharField(max_length=100,verbose_name='نام محصول')
header_pic = models.ImageField(upload_to="images/products",verbose_name='تصویر اصلی',
default='images/product/product1.png')
created_date =models.DateField(auto_now_add=True)
description = RichTextField(blank = True , null=True)
class Image(models.Model):
product = models.ForeignKey(Product, default=None, on_delete=models.CASCADE, related_name="product_images")
image = models.ImageField(upload_to="images/products/product-images",verbose_name='تصویر', null= True , blank=True)
#property
def get_image_url(self):
return self.image.url
serializer.py:
from django.db.models import fields
from rest_framework import serializers
from .models import Product, Image
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = "__all__"
class ProductSerializer(serializers.HyperlinkedModelSerializer):
images = serializers.SerializerMethodField()
def get_images(self, product):
return ImageSerializer(product.product_images.all(), many=True).data
class Meta:
model = Product
fields = ('id','images')
views.py:
class ImageViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
admin.py:
from django.contrib import admin
from .models import Product , ProductCategory ,Image
from . import models
admin.site.register(ProductCategory)
class ImageAdmin(admin.StackedInline):
model = models.Image
extra = 4
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
inlines = [ImageAdmin]
readonly_fields=('views', )
class Meta:
model = Product
#admin.register(Image)
class ImageAdmin(admin.ModelAdmin):
pass
template.html:
<div class="product-bottom-slider owl-theme owl-carousel" id="sync2">
{% for img in images %}
<div class="slide-top-item">
<div class="slide-inner">
<img src="{{img.image.url}}" alt="product">
</div>
</div>
{% endfor %}
</div>

Did you installed Pillow?
You can do this:
class Image(models.Model):
image = models.ImageField(upload_to='static/img/card_image/%Y/%m/%d', blank=True)
template.html:
<img src="/{{ product.image }}" height="" width="" alt="{{ product.product_name }}" class="img-fluid" alt="">

Related

Django Model Multi Select form not rendering properly

I am trying to display all the categories to appear as a list that I can click and select from, just an exact replica of what I have in my admin panel, but it still display's as a list that isn't clickable.
forms.py
class ProfileEditForm(forms.ModelForm):
"""
Form for updating Profile data
"""
class Meta:
model = Profile
fields = [
"first_name",
"last_name",
"about_me",
"profile_image",
"username",
"email",
"categories",
]
first_name = forms.CharField(label="First Name", max_length=63, required=False)
last_name = forms.CharField(label="Last Name", max_length=63, required=False)
about_me = forms.CharField(label="About Me", max_length=511, required=False)
email = forms.EmailField(label="Email", disabled=True)
username = forms.CharField(label="Username", disabled=True)
profile_image = forms.ImageField(required=False)
categories = forms.ModelMultipleChoiceField(
queryset=Category.objects.all(),
required=False,
widget=forms.CheckboxSelectMultiple(),
)
profile.models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile")
first_name = models.CharField(max_length=63, blank=False)
last_name = models.CharField(max_length=63, blank=False)
about_me = models.CharField(max_length=511, blank=True)
categories = models.ManyToManyField(
Category, related_name="user_categories", symmetrical=False
)
categories.models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=63)
created = models.DateTimeField(auto_now_add=True, blank=True, null=True)
last_modified = models.DateTimeField(auto_now=True, blank=True, null=True)
def __str__(self):
return self.name
def __unicode__(self):
return self.name
class Meta:
verbose_name_plural = "Categories"
settings.html
<div class='row'>
<div class="col s12 m6">
{{form.categories.errors}}
{{form.categories.label_tag}}
{{form.categories}}
</div>
</div>
What I hope to achieve
What I get
You need to create the form itself:
<form method='post'>
</form>
And print each field on a new line:
{{ form.as_p }}
is a security check.
{% csrf_token %}
In the view, I left get_context_data. In it, you can add values ​​to the context, for example, like this:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['rubrics'] = Car.objects.all()
Replace bboard with the name of the folder where your templates are placed.
I have this: templates/bboard which are in the application folder.
In the view for the form, the CreateView class is used, in which: template_name - the name of the template for displaying the page, form_class - the form class is indicated, success_url - where to return in case of successful saving of the form (in this case, this is the same page with the form), get_context_data - the template context (you can print it out and see what's inside).
And if your model has fields: first_name, last_name, about_me, email, username, profile_image, then it is enough that you have specified the fields variable in the class Meta class. You don't need to re-create them in the form.
template_name = 'bboard/tam_form.html'#bboard replace with your prefix
Templates
<form method='post'>
{{form.categories.errors}}
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value='adding'>
</form>
views.py
from .forms import *
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
class Profile(CreateView):
template_name = 'bboard/settings.html'#bboard replace with your prefix
form_class = ProfileEditForm
success_url = reverse_lazy('test')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
urls.py
from django.urls import path
from .views import *
urlpatterns = [
path('test/', Profile.as_view(), name='test'),
]
Update 13.11.2022
This is how my form looks like when I go to the address:
http://localhost:8000/test/
But the form is not submitted. I don't have much experience with this. I can assume that forms.ModelForm expects that the model has such fields, because if you delete the lines with email, username, profile_image and also remove them from the fields and add 'user' to the field, then the data will be saved in the database (checked).
As I said earlier, if the fields are declared in fields, then you do not need to define them again (if you leave them, the form will also be saved). This is what the form class looks like:
class ProfileEditForm(forms.ModelForm):
class Meta:
model = Profile
fields = [
'user',
'first_name',
'last_name',
'about_me',
'categories',
]
categories = forms.ModelMultipleChoiceField(
queryset=Category.objects.all(),
required=False,
widget=forms.CheckboxSelectMultiple(),
)

How to return a value from another model using the Django Rest Framework?

When I query all the comments of the post, I want to return the user's username.
My two Models:
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
post = models.ForeignKey(
Post, on_delete=models.CASCADE, null=False, blank=False)
title = models.TextField()
date = models.DateField(auto_now=True)
class User(AbstractUser):
objects = UserManager()
username = models.CharField(max_length=60, unique=True)
avi_pic = models.ImageField(
_('avi_pic'), upload_to=aviFile, null=True, blank=True)
My Comments Serializer:
class CommentSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField('get_username_from_user')
avi_pic = serializers.SerializerMethodField('get_avi_pic')
class Meta:
model = Comment
fields = '__all__'
def get_username_from_user(self, comment):
username = comment.user.username
return username
def get_avi_pic(self, comment):
request = self.context['request']
avi_pic = comment.user.avi_pic.url
return request.build_absolute_uri(avi_pic)
My Comments View:
class CommentView(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
serializer_class = CommentSerializer
# Get all comments from current post
def get(self, request):
post_id = request.data.get('id')
post = Post.objects.get(id=post_id)
comment = Comment.objects.filter(post=post).values()
serializer = CommentSerializer(comment)
return Response(serializer.data, status=status.HTTP_200_OK)
In my console I get: 'QuerySet' object has no attribute 'user'
Appreciate any help!!
In views.py:
comment = Comment.objects.filter(post=post)
In serializer.py:
def get_username_from_user(self, comment):
username = comment.user.username
return username
In views.py:
def get(self, request):
...
serializer = CommentSerializer(comment, many=True)
...
In my humble opinion, your problem is not having a ForeignKey for the "User" model, meaning whatever model you are trying to render doesn't have a column named 'user'. I'd do something like this:
models.py
class User(AbstractUser):
pass
def __str__(self):
return f"{self.username}"
class Comment(models.Model):
comment = models.TextField(max_length=300, null=True)
creation_date = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
whatever_name = models.ForeignKey(whatever_model_to_relate, on_delete=models.CASCADE, related_name="comments")
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = ['comment']
widgets = {
'comment': forms.Textarea(attrs={'rows':4, 'cols':100}),
}
views.py
#login_required
def whatever_function(request, id):
whatever_name = whatever_related_model.objects.get(id=id)
return render(request, "template.html", {
"whatever_name_for_template": whatever_name,
"commentform": CommentForm()
})
template.html
{% for comment in whatever_related_model.comments.all %}
<div class="card p-1 m-2 col-lg-12 col-sm-12">
<div class="card-body">
<h5 class="card-title">{{ comment.user }}</h5>
<h6 class="card-subtitle mb-2 text-muted">{{ comment.creation_date }}</h6>
{{ comment.comment }}
</div>
</div>
{% endfor %}
Hopefully I didn't get sidetracked from your question.

models.py order of the models gives NameError: name 'Category/Post' is not defined

I'm new to Django so this is probably a dumb question but,
when I put the class Category model above the class Post model I get an
NameError: name 'Post' is not defined error.
but when I try to put class Category model underneath the Post model (as in the code here) I get
categories = models.ManyToManyField(Category)
NameError: name 'Category' is not defined error.
models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE) #if is deleted than delete their posts
location = models.CharField(max_length=100, default="")
tags = TaggableManager()
likes = models.ManyToManyField(User, related_name='blog_posts')
categories = models.ManyToManyField(Category)
def total_likes(self):
return self.likes.count()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Category(models.Model):
post = models.ForeignKey(Post, related_name="categories")
name = models.CharField(max_length=20)
def __str__(self):
return self.name
admin.py
from django.contrib import admin
from .models import Post, Comment, Category #, Konum
# Register your models here.
admin.site.register(Post)
admin.site.register(Comment)
admin.site.register(Category)
#admin.site.register(Konum)
some of the code
<form method="GET" action=".">
<div class="form-group col-md-4">
<label for="category">Category</label>
<select id="category" class="form-control" name="category">
<option selected>Choose...</option>
{% for cat in categories %}
<option value="{{ cat }}">{{ cat }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary">Search</button>
</form>
views.py
def home(request):
context = {
"posts": Post.objects.all()
}
return render(request, 'blog/home.html', context)
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------#--------------------------------------------------------------------------------------------------------------------------------------
#--------------------------------------------------------------------------------------------------------------------------------------
def filter(request):
qs = Post.objects.all()
categories = Category.objects.all()
id_exact_query = request.GET.get('id_exact')
title_or_author_query = request.GET.get('title_or_author')
category = request.GET.get('category')
if is_valid_queryparam(category) and category != 'Choose...':
qs = qs.filter(categories__name=category)
context = {
'posts': qs,
'categories': Category.objects.all()
}
return render(request, 'blog/home.html', context)
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 199
class UserPostListView(ListView):
model = Post
template_name = 'blog/user_posts.html'
context_object_name = 'posts'
paginate_by = 199
def get_queryset(self):
return Post.objects.filter(author = user).order_by('-date_posted')
urls.py
from django.urls import path, re_path
from .import views
from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView, UserPostListView, TagIndexView, LikeView #, LocationPostListView
urlpatterns = [
path('', PostListView.as_view(), name="blog-home"), #has a empty strting bc its already processed blog part in main urls
path('user/<str:username>', UserPostListView.as_view(), name="user-posts"),
#--------------------------------------------------------------------------------------------------------------------------------------
#path('location/<str:loc>', LocationPostListView.as_view(), name="location-posts"),
#--------------------------------------------------------------------------------------------------------------------------------------
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),#pk means primary key like post 1 post 2 etc
path('post/new/', PostCreateView.as_view(), name='post-create'),
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
path('about/', views.about, name="blog-about"),
#--------------------------------------------------------------------------------------------------------------------------------------
path('tag/<slug:slug>/', TagIndexView.as_view(), name='tagged'),
path('like/<int:pk>', LikeView, name='like_post'),
#--------------------------------------------------------------------------------------------------------------------------------------
]
You can use a string literal to specify the model name of a model that still needs to be defined, so you can use ManyToManyField('Category') or ForeignKey('Post', on_delete=models.CASCADE) for example to refer to models not yet defined:
from django.conf import settings
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
location = models.CharField(max_length=100, default="")
tags = TaggableManager()
likes = models.ManyToManyField(settings.AUTH_USER_MODELS, related_name='liked_posts')
categories = models.ManyToManyField('Category')
It however does not seem to make much sense that a Category has a ForeignKey to a post: that would mean that a Category links to exactly one Post record?
You can for example use a ListView with:
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 199
def get_queryset(self):
qs = super().get_queryset()
if self.request.GET.get('category'):
return qs.filter(categories__name=self.request.GET['category'])
return qs
def get_context_data(self, *args, **kwargs):
context = super().get_queryset(*args, **kwargs)
context['categories'] = Category.objects.all()
return context
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

How get image from images model to home page in Django?

I am developing an ecommerce website with Django. In my home page displayed product cards as you see in the below image.
This product image in each card I take from my Product model (image field). When I hover over this image on the home page, the image is changing to another image. That is for I need another image, and I want to take the next image (display when I hover over) from my Product_images model. But I don't know how to do that.
urls.py
from django.urls import path
from . import views
from django.conf.urls.static import static
urlpatterns =
path('', views.home_page, name='amd-home'),
path('product/<int:id>/', views.product_detail, name='product-detail'),
path('about/', views.about, name='amd-about'),
]
views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView
from django.http import HttpResponse
from .models import Product, Product_image, Product_details
def home_page(request):
products = Product.objects.all()
images = Product_image.objects.all()
context = {'products':products, 'images':images}
return render(request, 'product/home.html', context)
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=200)
parent_id = models.IntegerField(default=0)
description = models.TextField()
image = models.ImageField(upload_to='uploads/')
def __str__(self):
return f'{self.name}'
class Brand(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=400)
image = models.ImageField(upload_to='uploads/')
def __str__(self):
return f'{self.name}'
class Product(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='uploads/', blank=True, null=True)
sku = models.CharField(max_length=200)
price = models.IntegerField(default=0)
price_old = models.IntegerField(default=0)
description = models.TextField()
status = models.BooleanField(default=False)
date_posted = models.DateTimeField(auto_now_add=True)
internal_storage = models.CharField(max_length=50, blank=True, null=True, default=None)
ram = models.CharField(max_length=50, blank=True, null=True, default=None)
user = models.ForeignKey(User, on_delete=models.CASCADE)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return f'{self.title}, {self.description}'
class Product_image(models.Model):
image = models.ImageField(upload_to='uploads/')
product = models.ForeignKey(Product, on_delete=models.CASCADE)
def __str__(self):
return f'{self.product.title} image'
home.html
my template file is a very large file, so I only insert the element where I get an image from the Product model (this code word fine), but I don't know how to write code to take images from my Product_image model.
{% for product in products %}
<img alt="" src="{{ product.image.url }}">
{% endfor %}
First, in your model you can give a related_name field like :
class Product_image(models.Model):
image = models.ImageField(upload_to='uploads/')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="product_images)
def __str__(self):
return f'{self.product.title} image'
Then you can access the product's images in template like:
{% for product in products %}
{% for image in product.product_images.all %}
<img alt="" src="{{ image.image.url }}">
{% endfor %}
{% endfor %}
PS: You dont have to return all Product_image quesryset from the view
Expanding on the answer, if you want to order the images there are different approaches you can take:
Method1:
class Product_image(models.Model):
image = models.ImageField(upload_to='uploads/')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="product_images)
time_created = models.DateTimeField(null=True, blank=True)
def __str__(self):
return f'{self.product.title} image'
class Meta:
ordering = ['time_created']
This will order the query set from first created to last . If you don't want to add a time created field you can also choose to order by id.
Method2:
Add a property to your Product model:
class Product:
....
#property
def sorted_image_set(self):
return self.product_images.order_by('time_created')
Then you can access this property from the template
{% for image in product.sorted_image_set %}
Method 3 :
Creating a custom template tag to support order_by in template
#register.filter
def order_by(queryset, args):
args = [x.strip() for x in args.split(',')]
return queryset.order_by(*args)
Then you can do :
{% for image in product.product_images|order_by:"time_created" %}
After the list has been ordered you can access it just by the array indexes like for example images[0] or images[1]
change src in your HTML
{% for product in products %}
<a href="{% url 'product-detail' product.id %}"><img alt="" src="/media/uploads/{{
product.image }}"></a>
{% endfor %}
I have assumed that you have MEDIA_URL=/media/ in your settings. Change this in src if you have different MEDIA_URL

Display user`s protrait from model in template in django

I want to display the the user protrait of the user in the templates.
models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
date_of_birth = models.DateField(blank=True, null=True)
photo = models.ImageField(upload_to='user/%Y/%m/%d/', blank=True)
views.py
#login_required
def dashboard(request):
# Display all actions by default
actions = Action.objects.exclude(user=request.user)
following_ids = request.user.following.values_list('id',
flat=True)
if following_ids:
# If user is following others, retrieve only their actions
actions = actions.filter(user_id__in=following_ids)
actions = actions.select_related('user', 'user__profile')\
.prefetch_related('target')[:10]
return render(request,
'account/dashboard.html',
{'section': 'dashboard',
'actions': actions})
tempate:
<img src="{{ Profile.photo.url }} " height="40" width="40"/>
You can use:
<img src="{{ request.user.profile.photo.url }} " height="40" width="40"/>
as you have a One-to-One relationship between the django's User model and your Profile model.

Categories

Resources