On my main page i want to show a category just like {{ post.body }} but instead {{ post.body }} would be {{ post.category }} here.
{% extends "base.html" %}
{% block title %}Articles{% endblock title %}
{% block content %}
{% for post in post_list %}
<div class="card">
<div class="card-header">
<span class="font-weight-bold">{{ post.title }} |
</span> ·
<span class="text-muted">by {{ post.author }} |
{{ post.created_at }}</span>
</div>
<div class="card-body">
<!-- Changes start here! -->
<p>{{ post.body }}</p>
<p>{{ post.category }}</p>
Edit |
Delete |
Category
</div>
<div class="card-footer">
{% for comment in post.comment_set.all %}
<p>
<span class="font-weight-bold">
{{ comment.author }} ·
</span>
{{ comment }}
</p>
{% endfor %}
</div>
<!-- Changes end here! -->
</div>
<br />
{% endfor %}
{% endblock content %}
but cannot to figure out how. But cannot figure out how.
Here is my models
from django.conf import settings
from django.db import models
from django.urls import reverse
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return (self.name)
def get_absolute_url(self):
return reverse("home")
class Post(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
category = models.ManyToManyField(Category, related_name='categories')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("post_detail", kwargs={"pk": self.pk})
class Comment(models.Model):
article = models.ForeignKey(Post, null=True, blank=True, on_delete=models.CASCADE)
comment = models.CharField(max_length=140)
author = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,)
def __str__(self):
return self.comment
def get_absolute_url(self):
return reverse("post_list")
Thanks in advance
Added {{ post.category }} in here
{% extends "base.html" %}
{% block title %}Articles{% endblock title %}
{% block content %}
{% for post in post_list %}
<div class="card">
<div class="card-header">
<span class="font-weight-bold">{{ post.title }} |
</span> ·
<span class="text-muted">by {{ post.author }} |
{{ post.created_at }}</span>
</div>
<div class="card-body">
<!-- Changes start here! -->
<p>{{ post.body }}</p>
<p>{{ post.category }}</p>
Edit |
Delete |
Category
</div>
<div class="card-footer">
{% for comment in post.comment_set.all %}
<p>
<span class="font-weight-bold">
{{ comment.author }} ·
</span>
{{ comment }}
</p>
{% endfor %}
</div>
<!-- Changes end here! -->
</div>
<br />
{% endfor %}
{% endblock content %}
expected to see a category instead got post.category.None
To show the category in the HTML just like a property, you can add a property method in the model class and use it HTML, like this:
class Post(models.Model):
...
#property
def categories(self):
return ', '.join([x.name for x in self.category.all()]
Then use it template like:
{{ post.categories }}
Alternatively as it is a ManyToMany field, you can iterate through it in the template and render the categories:
{{ for c in post.category }} {{ c.name }} {% if not forloop.last %}, {% endif %} {{ endfor }}
Explanation of property method: property method allows you to convert a function to an attribute of the object of that class. self in the property method refers to itself (or the object itself). using self.category.all(), you are accessing the manytomany relation between Post and Category class. I am running a loop and access each of the Category objects connected to the Post object (the main object which are using in the template/html) and I am joining the names of the Category objects' names to form a comma separated string.
Related
I have an app that shows the history of products ordered in a restaurant, and to get the data of the restaurant, i have the following view:
def restaurant_orders(request):
restaurant = get_restaurant(request.user.restaurant.user_id)
products = list(restaurant.restaurante.values_list("id"))
items = Item.objects.filter(product_id__in=products).order_by('order_id')
context = {'items': items}
return render(request, 'pages/orders_restaurant.html', context)
In template i'm displaying as follows:
<div class="col-lg-7">
<h2>Histórico de Pedidos</h2>
{% for value in items %}
<ul class="list-group list-group-flush"></ul>
<li class="list-group-item bg-light">
<h4>Order {{ value.order_id }}</h4>
{{ value.quantity }} X {{ value.product }}
<span class="float-right">R$ {{ value.price }}</span>
<li class="font-weight-bold list-group-item bg-light">Client Name
<span class="float-right">{{ value.order.name }}</span>
</li>
</li>
<br>
{% endfor %}
</div>
but doing it this way I get each data separate, and I would like to know if there is any way to group the data that has a field with the same value. The result I getting now is this, and I would like something like this.
model.py
class Item(models.Model):
order = models.ForeignKey(Order, related_name="items", on_delete=models.CASCADE)
product = models.ForeignKey(Product, related_name="order_items", on_delete=models.CASCADE)
price = models.DecimalField(max_digits=5, decimal_places=2)
quantity = models.PositiveIntegerField(
validators= [
MinValueValidator(1),
MaxValueValidator(20),
]
)
You need loop over a Order model query.
You can filter it throught the related name property in the Item model.
orders = Order.objects.filter(items__product_id__in=products)
Then, in your template, you can loop through the orders and the items in each one's:
<ul> {# apply your markup... #}
{% for order in orders %}
<li>
{{ order }}
<ul>
{% for item in order.items.all %}
<li>{{ item.product }} {# ... #}
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Trying to use a paginator in a view. Getting none from page = request.GET.get('page'). As is, the call to paginator limits posts on a page properly, but any call to page sequence henceforth fails. Pages will display, but there will be nothing form pagination.html displayed. For clarity, Base.html is the base template from which all others inherit. list.html is the page where I expect to see pagination.html displayed.
This code is based off of the django manual. Is there something else that needs to be setup to give the requests querydict a key of 'page' or a better way to paginate?
views.py
from django.shortcuts import render, get_object_or_404
from .models import Post
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def post_list(request):
object_list = Post.published.all() #a list of the posts
paginator = Paginator(object_list, 4) # 4 posts in each page
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer deliver the first page
posts = paginator.page(1)
except EmptyPage:
# If page is out of range deliver last page of results
posts = paginator.page(paginator.num_pages)
return render(request,'blog/post/list.html',{'page': page,'posts': posts})
pagination.html
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
Previous
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}.
</span>
{% if page.has_next %}
Next
{% endif %}
</span>
</div>
base.html
#...
<div id="content">
{% block content %}
{% include "pagination.html" with page=posts %}
{% endblock %}
</div>
#...
list.html
{% extends "blog/base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% endblock %}
When you fill a block in a child template it replaces the super template's block's content (not literally the block named content here). If you want to keep the parent's block along with some extra content, you should use {{ block.super }}, i.e. list.html should be:
{% extends "blog/base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<h2>
<a href="{{ post.get_absolute_url }}">
{{ post.title }}
</a>
</h2>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{{ block.super }}
{% endblock %}
I have problem with accessing by the link. I am using easy-thumbnail framework, and I created simple view for user list to list all existing users.
Error message:
django.template.exceptions.TemplateSyntaxError: Variable
'user.profile.photo' is an invalid source.
Django Traceback throws me to this view.
views.py file:
#login_required
def user_list(request):
users = User.objects.filter(is_active=True)
return render(request,
'account/user/list.html',
{'section': 'people',
'users': users})
urls.py file:
path('users/', views.user_list, name='user_list'),
template list.html:
{% extends "base.html" %}
{% load thumbnail %}
{% block title %}People{% endblock %}
{% block content %}
<h1>People</h1>
<div id="people-list">
{% for user in users %}
<div class="user">
<a href="{{ user.get_absolute_url }}">
<img src="{% thumbnail user.profile.photo 180x180 %}">
</a>
<div class="info">
<a href="{{ user.get_absolute_url }}" class="title">
{{ user.get_full_name }}
</a>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
sample code from base.html:
<li {% if section == "people" %}class="selected"{% endif %}>
People
</li>
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='users/%Y/%m/%d/',
blank=True)
def __str__(self):
return f'Profile for user {self.user.username}'
Thank you for the helping in advance.
Reviewed the documentation https://easy-thumbnails.readthedocs.io/en/latest/usage/#templates. Creates a thumbnail from an object (usually a file field). I tried it and it helped solve my problem:
<img src="{{ user.profile.photo.url }}">
I had this problem too. You can solve the problem with the following solution:
Instead of users = User.objects.filter(is_active=True), use profiles = Profile.objects.all()
Change as follows: return render(request,'account/user/list.html',{'section': 'people', 'profiles': profiles}).
In the list.html file, change as follows:
{% extends 'base.html' %}
{% load thumbnail %}
{% block title %}People{% endblock %}
{% block content %}
<h1>People</h1>
<div id="people-list">
{% for profile in profiles %}
<div class="user">
<a href="{{ profile.user.get_absolute_url }}">
<img src="{% thumbnail profile.photo 180x180 %}" alt="{{ profile.user.last_name }}">
</a>
<div class="info">
<a href="{{ profile.user.get_absolute_url }}" class="title">
{{ profile.user.get_full_name }}
</a>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
I am always getting all the food items in all Categories.
I want to display each Category with each food item.
Need to display Category with item belonging to that Category.
Please someone help me on this issue to resolve. I have tried looking over internet but couldn't find the solution to it
models.py
class Category(models.Model):
category_name = models.CharField(max_length=50)
def __str__(self):
return self.category_name
class Menu(models.Model):
dish_name = models.CharField(max_length=200, verbose_name='Name of Dish')
Desc = models.TextField(verbose_name='Dish Description')
Amount = models.IntegerField(null=False, blank=False, verbose_name='Amount of Dish')
date_posted = models.DateTimeField(default=timezone.now, verbose_name='Dish Date Posted')
category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1)
def __str__(self):
return self.dish_name
views.py
def menu(request):
products = Menu.objects.all()
categories = Category.objects.all()
data = {}
data['products'] = products
data['categories'] = categories
template = 'food/menu.html'
return render(request, template, data)
html
{% for category in categories %}
{% if categories %}
<div class="col-xs-12 col-sm-6">
<div class="menu-section">
<h2 class="menu-section-title">{{ category.category_name }}</h2>
<hr>
{% endif %}
{% for i in products %}
<div class="menu-item">
<div class="menu-item-name">{{ i.dish_name}}</div>
<div class="menu-item-price">Rs {{ i.Amount}}</div>
<div class="menu-item-description">{{ i.Desc}}</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
you are not filtering products based on categoris.
I would suggest doing something like this with data:
data = {
'categories': {
category: Menu.objects.filter(category=category) for category in categories
}
}
and in html you can:
{% for category, products in categories.items %}
{% if products %}
<div class="col-xs-12 col-sm-6">
<div class="menu-section">
<h2 class="menu-section-title">{{ category.category_name }}</h2>
<hr>
{% for i in products %}
<div class="menu-item">
<div class="menu-item-name">{{ i.dish_name}}</div>
<div class="menu-item-price">Rs {{ i.Amount}}</div>
<div class="menu-item-description">{{ i.Desc}}</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endfor %}
I want to express the detailview as the picture. I want to code the box part in the template of the picture.
enter image description here
It is as follows now.
views.py
#login_required
def product_detail(request, id, product_slug=None):
product = get_object_or_404(Product, id=id, slug=product_slug)
return render(request, 'shop/detail.html', {'product': product})
I think it should be modified to class. I would like to explain it by representing DetailView and ListView together in detail_template. I modified only views.py as shown below.
class ProductDetailView(DetailView, ListView):
model = Product
template_name = 'shop/detail.html'
context_object_name = 'latest_question_list'
#login_required
def get_queryset(self, id, product_slug=None):
product = get_object_or_404(Product, id=id, slug=product_slug)
return render(self, 'shop/detail.html', {'product': product})
This error occurs. AttributeError: 'ProductDetailView' object has no attribute 'user'
urls.py
urlpatterns = [
.........
path('<int:id>/<product_slug>/', product_detail, name='product_detail'),
.........
]
detail.html
{% extends 'base.html' %}
{% block title %}Product Detail{% endblock %}
{% block content %}
<div class="col">
<div class="alert alert-info" role="alert">Detail</div>
<div class="container">
<div class="row">
<div class="col-4">
<img src="{{product.image.url}}" width="100%">
</div>
<div class="col">
<h1 class="display-6">{{product.cname}}</h1>
<p class="card-text">{{product.pname}}</p>
<h5><span class="badge badge-secondary">Description</span>{{product.description|linebreaks }}</h5>
{% if product.author.username == user.username %}
Update
Delete
{% endif %}
{% if product.author.username != user.username %}
Inquiry
{% endif %}
Continue shopping
</div>
</div>
</div>
<p></p>
<div class="col">
<div class="alert alert-info" role="alert">Products added by registrants</div>
<div class="container">
{% for product in products %}
<div class="row">
{% if product.user.username == user.username %}
<div class="col-4">
<img src="{{product.image.url}}" width="auto" height="250">
</div>
<div class="col">
<h1 class="display-6">{{product.pname}}</h1>
<h5><span class="badge badge-secondary">Description</span>{{product.description|linebreaks}}</h5>
</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endblock %}
Please help me how to fix it. I would also appreciate it if you would recommend any textbooks that I can refer to.
I have a similar situation. I wanted to display create form, detail and list on the same page:
urls:
example_urlpatterns = [
path('', views.ExampleCreateView.as_view(), name='_list'),
path('new/', views.ExampleCreateView.as_view(), name='_create'),
path('<int:pk>/', views.ExampleCreateView.as_view(), name='_detail'),
path('<int:pk>/del', views.ExampleDeleteView.as_view(), name='_del'),
]
urlpatterns = [
# ...
path('example/', include(example_urlpatterns)),
# ...
]
As you can see, I have two views, ExampleCreateView (also providing detail and list) and ExampleDeleteView for deleting. ExampleCreateView is primarily a create view:
class ExampleCreateView(CreateView):
template_name = 'example.html'
form_class = ExampleCreateForm
model = Example
def form_valid(self, form):
pass # Stuff to do with a valid form
# add user info from request to the form
def get_form_kwargs(self, *args, **kwargs):
kwargs = super().get_form_kwargs(*args, **kwargs)
kwargs['user'] = self.request.user
return kwargs
# Create appropriate context
def get_context_data(self, **kwargs):
kwargs['object_list'] = Example.objects.order_by('ip') # list
try: # If we have pk, create object with that pk
pk = self.kwargs['pk']
instances = Example.objects.filter(pk=pk)
if instances:
kwargs['object'] = instances[0]
except Exception as e:
pass # No pk, so no detail
return super().get_context_data(**kwargs)
Because I'm inheriting from CreateView, all the form processing is taken care of by default.
Adding the line kwargs['object_list'] =... makes it work as a list view, and the try block after that line makes it work as a detail view.
In the template all relevant parts are displayed:
{% if object %}
{% comment %}
...display the object... (Detail section)
{% endcomment %}
{% endif %}
{% if form %}
{% comment %}
...display the form... (Create section)
{% endcomment %}
{% endif %}
{% if object_list %}
{% comment %}
...display the list... (List section)
{% endcomment %}
{% endif %}
Let me know if this helps
Currently I have applied like this
views.py : modified
#method_decorator(login_required, name="dispatch")
class ProductDetailView(DetailView):
model = Product
template_name = 'shop/detail.html'
def get_context_data(self, **kwargs):
context = super(ProductDetailView, self).get_context_data(**kwargs)
context['product_list'] = Product.objects.all()
return context
index = ProductDetailView.as_view()
detail.html : modified
<div class="col">
<div class="alert alert-info" role="alert">Products added by this registrant</div>
<div class="container">
{% for prdt in product_list %}
{% if product.author.username == prdt.author.username %}
{% if product.pname != prdt.pname %}
{{ prdt }}<br>
{% endif %}
{% endif %}
{% endfor %}
</div>
enter image description here