I'm trying to iterate through the images in my categories but I'm getting 'ImageFileDescriptor' object has no attribute 'objects' error
models.py
class Category(models.Model):
category_title = models.CharField(max_length=200)
category_image = models.ImageField(upload_to="category")
category_description = models.TextField()
slug = models.SlugField(max_length=200, unique=True, default=1)
def get_image_path(self, filename):
gallery_path = os.path.abspath(
os.path.join(settings.MEDIA_ROOT, self.slug))
if not os.path.isdir(gallery_path):
os.mkdir(gallery_path)
return os.path.join(gallery_path, filename)
gallery = models.ImageField(upload_to=get_image_path)
def create_directory(self):
gallery_path = os.path.abspath(
os.path.join(settings.MEDIA_ROOT, self.slug))
if not os.path.isdir(gallery_path):
os.mkdir(gallery_path)
def save(self, *args, **kwargs):
if not self.pk:
self.create_directory()
super().save(*args, **kwargs)
def delete(self, *args, **kwargs):
shutil.rmtree(os.path.join(settings.MEDIA_ROOT, self.slug))
# os.rmdir(os.path.join(settings.MEDIA_ROOT, self.slug))
super().delete(*args, **kwargs)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.category_title
views.py
def category_detail_view(request, slug):
category = get_object_or_404(Category, slug=slug)
context = {
"gallery": Category.gallery.objects.all(),
}
return render(request, 'main/category_detail.html', context)
category_detail.html
{% for image in gallery %}
<div class="col-md-4">
<a href="{{ image.url }}"> <img src="{{ image.url }}" class="img-responsive img-thumbnail" width="304" height="236"/>
</a>
</div>
{% endfor %}
For some reason creating galleries in Django has been difficult for me. It seems as if there is no standard way that most people are doing this. Even if I get this to work, I don't see how one could manage the uploaded images from the admin panel. ex upload delete arrange etc
If you could shed some light on this for me that would be awesome
Related
I have looked for an answer and all I can find are answers related to forms. I haven't seen anything related to a model object. I have a model called PanelType and I am trying to loop through every object to display relevant information pertaining to each panel type on a html template. I have created 3 PanelType objects through the admin page, so there is not "None". I believe I have this setup correctly but it's returning an error of returning None objects. I'd appreciate any help.
models.py
class PanelType(models.Model):
name = models.CharField('Name', max_length=150, default='')
slug = models.SlugField(unique=True)
description = models.TextField('Description', null=True, blank=True)
date_created = models.DateTimeField(
'Date Created', auto_now_add=True, null=True)
display = models.BooleanField(default=True)
image = models.ImageField('Panel Image', null=True, blank=True)
def get_absolute_url(self):
return reverse("model_detail", kwargs={"pk": self.pk})
def __str__(self):
return f'{self.name}'
def save(self, *args, **kwargs):
if not self.slug or self.slug != slugify(self.name):
self.slug = slugify(self.name)
return super().save(*args, **kwargs)
views.py
def services(request):
context = {'panels': PanelType.objects.all()}
render(request, 'app/services.html', context)
html
<div class="service-container">
{% for panel in panels %}
<div class="card">
<div class="serviceBx" data-text="{{panel.name}}">
<img src="{{panel.image.url}}" alt="">
</div>
<div class="service-content">
<div>
<h3>{{panel.name}}</h3>
<p>{{panel.description}}</p>
Get Quote
</div>
</div>
</div>
{% endfor %}
</div>
You need to return the result of the render(…) function:
def services(request):
context = {'panels': PanelType.objects.all()}
# ↓ return the HttpResponse
return render(request, 'app/services.html', context)
Helloo,
I have created a comment button for my blog posts and I am getting "This page isn’t working" after submitting the comment button and I don't know the reason.
I can add comments from the admin but can not submit as a user from website
I am not sure what needs to be changed in the views.py
class PostDetailView(DetailView):
model = Post
template_name = "post_detail.html"
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data()
post = get_object_or_404(Post, id=self.kwargs['pk'])
comments = Comment.objects.filter(post=post).order_by('-id')
total_likes = post.total_likes()
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
if self.request.method == 'POST':
comment_form = CommentForm(self.request.POST or None)
if comment_form.is_valid():
content = self.request.POST.get('content')
comment = Comment.objects.create(
post=post, user=request.user, content=content)
comment.save()
return HttpResponseRedirect(post.get_absolute_url())
else:
comment_form = CommentForm()
context["total_likes"] = total_likes
context["liked"] = liked
context["comments"] = comments
context["comment_form"] = comment_form
return context
class PostListView(ListView):
model = Post
template_name = "score.html"
ordering = ['-date_posted']
context_object_name = 'posts'
paginate_by = 5
here is the template
<form method="post" class="comment-form" action=".">
{% csrf_token %}
{{ comment_form.as_p }}
{% if request.user.is_authenticated %}
<input type="submit" value="Submit" class="btn btn-outline-success">
{% else %}
<input type="submit" value="Submit" class="btn btn-outline-success" disabled> You must be Logged in to Comment
{% endif %}
</form>
here is the models.py
class Post(models.Model):
designer = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
likes = models.ManyToManyField(
User, related_name='liked')
slug = models.SlugField(blank=True, null=True, max_length=120)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse("score:post-detail", kwargs={'slug': self.slug})
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
# reply = models.ForeignKey('Comment', null=True, related_name="replies")
content = models.TextField(max_length=160)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{}-{}'.format(self.post.title, str(self.user.username))
You are using parent class DetailView for your PostDetailView. However, there is no post method defined in DetailView. I believe when you submit your comment you are getting HTTP 405 method not allowed. You can check it from browsers developer tools.
I advice you to use a separate view for saving your comments.
class PostDetailView(DetailView):
model = Post
template_name = "post_detail.html"
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data()
post = get_object_or_404(Post, id=self.kwargs['pk'])
comments = Comment.objects.filter(post=post).order_by('-id')
total_likes = post.total_likes()
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
comment_form = CommentForm()
context["total_likes"] = total_likes
context["liked"] = liked
context["comments"] = comments
context["comment_form"] = comment_form
return context
class PostCommentCreateView(CreateView):
model = Comment
fields = ['content']
def get(self, request, post_id, *args, **kwargs):
raise Http404
def post(self, request, post_id, *args, **kwargs):
self.post = Post.objects.get_or_404(id=post_id)
self.user = request.user
super().post(request, *args, **kwargs)
def form_valid(self, form):
form.instance.post = self.post
form.instance.user = self.user
return super(StudentCreateView, self).form_valid(form)
You need to provide action to your form:
<form action={% url 'post-comment' Post.id %} method="post" class="comment-form" action=".">
{% csrf_token %}
{{ comment_form.as_p }}
{% if request.user.is_authenticated %}
<input type="submit" value="Submit" class="btn btn-outline-success">
{% else %}
<input type="submit" value="Submit" class="btn btn-outline-success" disabled> You must be Logged in to Comment
{% endif %}
</form>
To make your create view to work you need to add the following line to your urls:
path('post/<int:post_id>/comment', views.PostCommentCreateView.as_view(), name='post-comment'),
you need to add new url for the comment page with the post-comment name as well. And some additional imports.
I think you are missing a total_likes() method in Post model:
class Post(models.Model):
# ...
def total_likes(self):
return self.likes.all().count()
Alternatively, you can change the view:
class PostDetailView(DetailView):
def get_context_data(self, *args, **kwargs):
# ...
total_likes = post.likes.all().count()
# ...
I have an Image model and a Category model. I want to display only images of the corresponding category in my category_detail view.
models.py
class Category(models.Model):
category_title = models.CharField(max_length=200)
category_image = models.ImageField(upload_to="category")
category_description = models.TextField()
slug = models.SlugField(max_length=200, unique=True, default=1)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.category_title
class Image(models.Model):
category = models.ForeignKey(Category, on_delete="CASCADE")
image = models.ImageField()
caption = models.CharField(max_length=250)
class Meta:
verbose_name_plural = "Images"
def __str__(self):
return str(self.image)
views.py
def category_detail_view(request, slug):
category = get_object_or_404(Category, slug=slug)
context = {
"gallery": Image.objects.filter(Category),
}
return render(request, 'main/category_detail.html', context)
category_detail.html
{% for image in gallery %}
<div class="col-md-4">
<a href="{{ image.url }}"> <img src="{{ image.url }}" class="img-responsive img-thumbnail" width="304" height="236"/>
</a>
</div>
{% endfor %}
You can filter these with:
def category_detail_view(request, slug):
category = get_object_or_404(Category, slug=slug)
context = {
'gallery': Image.objects.filter(category=category),
}
return render(request, 'main/category_detail.html', context)
or without fetching the category in a separate query:
You can filter these with:
def category_detail_view(request, slug):
context = {
'gallery': Image.objects.filter(category__slug=slug),
}
return render(request, 'main/category_detail.html', context)
I overrided get_results method of autocomplete-light as follow to have single tag in bold:
views.py
class TagAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self, **kwargs):
qs = Tag.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
def get_results(self, context):
results_list = []
for result in context["object_list"]:
text2show = "{}"
tag_occurrences = result.org_set.count()
if tag_occurrences == 1:
text2show = '<b>{}</b>';
results_list.append({
"id": self.get_result_value(result),
"text": format_html(text2show, self.get_result_label(result)),
"selected_text": format_html(text2show, self.get_selected_result_label(result)),
})
return results_list
Everything works perfectly if I search a tag, but if I try to view a stored event, all its tags are represented without any style.
forms.py
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = "__all__"
widgets = {
'tags': autocomplete.ModelSelect2Multiple(url='myapp:tag-autocomplete', attrs={'data-html': True})
}
def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs)
# if I try to edit an event
if self.instance:
# self.fields['tag'].queryset = ???
And this is a piece of my template:
<form method="post" class="form-horizontal">
{% csrf_token %}
{% for field in myform %}
<div class="form-group form-group-lg">
<label for="{{ field.id_for_label }}" class="col-sm-4 control-label">{{field.label}}</label><div class="col-sm-4">
{{ field }}
</div>
</div>
{% endfor %}
{{ myform.media }}
</form>
Is there a way to manipulate self.fields['tag'].queryset queryset to show field names in bold or another elegant solution?
Thanks.
update
models.py
class Tag(models.Model):
name = models.CharField('Tag name', max_length=255)
class Meta:
ordering = ['name']
def __init__(self, *args, **kwargs):
return mark_safe("<b>%s</b>" % self.name)
def __str__(self):
return self.name
I m beginner.
I'm trying to access a related item of the model Product in the template layer of a ProductDetailView. How can I retrieve the ImageFields of the Products' Brand's BrandImages? I have to traverse one forward and one reverse ForeignKey.
Edited to include get_logo_url
What is wrong with the get_logo_url function?
products/models.py
class Product(models.Model):
brand = TreeForeignKey('Brand', verbose_name='parent category', related_name='products', default='')
title = models.CharField(max_length=120)
description = models.TextField(max_length=500, blank=True, null=True)
price = models.DecimalField(decimal_places=2, max_digits=20)
active = models.BooleanField(default=True)
category = TreeForeignKey('Category', verbose_name='parent category', related_name='products', default='')
slug = models.SlugField(default='')
objects = ProductManager()
class Meta:
unique_together = ('slug', 'category')
def get_absolute_url(self):
return reverse("product_detail", kwargs={"pk":self.pk})
def __unicode__(self):
return self.title
def get_image_url(self):
img = self.productimage_set.first()
if img:
return img.image.url
return img
brands/models.py
def image_upload_to(instance, filename):
title = instance.brand.title
slug = slugify(title)
file_extension = filename.split(".")[1]
new_filename = "%s.%s" % (instance.id, file_extension)
return "products/%s/%s" % (slug, new_filename)
class BrandImage(models.Model):
brand = models.ForeignKey('Brand', related_name='brandimages')
is_slider = models.BooleanField(default=False)
is_featured = models.BooleanField(default=False)
is_logo = models.BooleanField(default=False)
image = models.ImageField(upload_to=image_upload_to)
def __unicode__(self):
return self.brand.title
def get_logo_url(self):
if is_logo:
img = self.brandimage_set.first()
if img:
return img.image.url
return img
def thumb(self):
if self.image:
return u'<img src="%s" width=120 height=120 />' % (self.image.url)
else:
return u'No image file found'
thumb.allow_tags = True
class Brand(MPTTModel):
title = models.CharField(max_length=50, default='')
parent = TreeForeignKey('self', null=True, blank=True, verbose_name='parent brand', related_name='brands')
slug = models.SlugField(unique=True)
def get_absolute_url(self):
return reverse('brands', kwargs={'path': self.get_path()})
def __unicode__(self):
return self.title
template
<div class="rightpart">
<div class="prodbrand h2">
<h1>{{ product.brand }}</h1>
<div class="brandlogo">
{% for image in product.brand.brandimages.all %}
<img src="{{image.get_logo_url }}"/>
{% endfor %}
</div>
</div>
<div class="prodtitle"><h2>{{ product.title }}</h2>
</div>
views.py
class ProductDetailView(DetailView):
model = Product
template_name = 'products/product.html'
def get_context_data(self , *args , **kwargs):
context = super(ProductDetailView , self).get_context_data(*args,**kwargs)
instance = self.get_object()
context["related"] = Product.objects.get_related(instance)
return context
urls.py
url(r'^$', ProductDetailView.as_view(), name='products'),
is there a way to access foreign fields in django templates like this?
As you are using a ListView to display your products there's several things to notice:
get_context_data() must return a dictionary: return context is missing
super().get_context_data should be called with *args,**kwargs incase you decide to subclass the ProductListView at a later point in time.
super().get_context_data will contain a object_list key which contains the list of objects returned by get_queryset(), in your case objects of class Product.
When accessing a property from a template, django will attempt to call it without parameters if it is callable. This is often useful e.g.: for {{ request.user.is_authenticated }} or product.brand.brandimages.all
Your template should look like this:
product_list.html
{% for product in object_list %}
<div class="rightpart">
<div class="prodbrand h2">
<h1>{{ product.brand }}</h1>
<div class="brandlogo">
{% for image in product.brand.brandimages.all %}
<img src="{{image.image.url}}"/>
{% endfor %}
</div><!-- End brandlogos -->
</div><!-- End prodbrand -->
<div class="prodtitle">
<h2>{{ product.title }}</h2>
</div>
</div><!-- End rightpart -->
{% endfor %}
Take into account that this will incur several database lookups from the template. You generally want to avoid the case that your presentation layer reaches into the database, which is why you should prefer to do the database lookup in the corresponding View. Also for property access consider using select_related and prefetch_related as appropriate to avoid unneeded database queries.
views.py
class ProductListView(ListView):
model = Product
queryset = Product.objects.all().active()
def get_context_data(self, *args, **kwargs):
context = super(ProductListView, self).get_context_data(*args, **kwargs)
context["now"] = timezone.now()
return context
def get_queryset(self, *args, **kwargs):
# We call super() for potential subclasses
queryset = super(ProductListView, self).get_context_data(*args, **kwargs)
queryset.prefetch_related('brand__brandimages')
return queryset