I'm trying to build a structure using Django Rest Framework to show some data in my html templates. But I can't show data from a model with ForeignKey.
My structure should be like this:
{% for category in categories %}
{{ category.category }} #category is the variable name
{% for channel in category.channel_set.all %}
{{ channel.title }}
{{ endfor }}
{{ endfor }}
But I can't print the channel variables in html files.
models.py:
class Category(models.Model):
user = models.ForeignKey(
'auth.User',
on_delete=models.DO_NOTHING,
unique=False,
)
category = models.CharField(
max_length=255,
unique=True,
)
_id = models.ObjectIdField(auto_created=True, serialize=False)
event_creation = models.DateTimeField(auto_now=True)
event_updated = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'category'
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.category
class Channel(models.Model):
user = models.ForeignKey(
'auth.User',
on_delete=models.DO_NOTHING,
)
_id = models.ObjectIdField(auto_created=True, serialize=False)
date_creation = models.DateTimeField(auto_now=True)
date_updated = models.DateTimeField(auto_now=True)
category = models.ForeignKey(
Category,
max_length=255,
on_delete=models.PROTECT,
related_name='channel',
unique=False,
to_field='category',
)
channel = models.CharField(
max_length=255,
unique=True,
)
def __str__(self):
return self.category
views.py:
class Gallery(viewsets.ModelViewSet):
renderer_classes = [TemplateHTMLRenderer]
template_name = '/gallery.html'
queryset = Category.objects.all()
queryset2 = Channel.objects.all()
permission_classes = [permissions.IsAuthenticated]
def get(self, request, **kwargs):
kwargs['categories_list'] = self.queryset
serializer = CategorySerializer(self.queryset,many=True)
serializer2 = ChannelSerializer(self.queryset2,many=True)
return Response({
'categories':serializer.data,
'channels':serializer2.data
})
# #login_required(login_url='/login/')
def list(self, request):
queryset = Category.objects.all()
response = {'categories': queryset}
return Response({'categories':queryset})
serializers.py:
class CategorySerializer(serializers.ModelSerializer):
# _id = serializers.ReadOnlyField()
categories = Category()
class Meta:
model = Category
fields = '__all__'
class ChannelSerializer(serializers.ModelSerializer):
# _id = serializers.ReadOnlyField()
channels = Channel()
class Meta:
model = Channel
fields = '__all__'
gallery.html:
{% extends "/model-page.html" %}
{% load core_tags %}
{% block content %}
<h1> co. </h1>
<h2> Last Archives </h2>
<a href="/category-api/">
<button type="button"> New input </button>
</a>
<ul>
{% for category in categories %}
<td> {% underscoreTag category "_id" as category_id %} </td>
<div {% if category.get_pass_event %} style="color: red "{% endif %}>
<li>{{ category.category }} - {{ category.get_creation }}
<ul>
<li>{{ category }}</li>
<ul>
<div>
{% for channel in category.channel_set.all %}
<li>Teste {{ channel.title }}</li>
{% endfor %}
</div>
</ul>
</ul>
Edit /
Delete
</li>
</div>
{% endfor %}
</ul>
{% endblock %}
I had trying for {% for channel in category.channel_set.all %},{% for channel in category.channels_set.all %}, {% for channel in category.channel.all %} and {% for channel in category.channels.all %} and any of these worked for me.
Another info from my project is that I'm using Django (because my database is MongoDB).
I think the code of the serializers isn't correct.
class ChannelSerializer(serializers.ModelSerializer):
class Meta:
model = Channel
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
channel = ChannelSerializer(many = True, read_only = True)
class Meta:
model = Category
fields = '__all__'
And in the html, some attributes don't exist in the models. channel_set should be changed into channel and channel.title into channel.channel.
There are no names like channel_set, title in the models.
...
<ul>
<div>
{% for channel in category.channel %}
<li>Teste {{ channel.channel }}</li>
{% endfor %}
</div>
</ul>
...
Or you should modify the names in the models.
Related
I have one simple model called Product
class Product(models.Model):
title = models.CharField(max_length=150)
def __str__(self):
return self.title
And one other model called ExternalProduct
class ExternalProduct(models.Model):
title = models.CharField(max_length=100)
external_id = models.CharField(max_length=25)
internal_product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
blank=True,
related_name='external_products',
)
price = models.IntegerField()
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, blank=True)
image_url = models.CharField(max_length=255)
product_url = models.CharField(max_length=255)
store = models.CharField(max_length=50)
active = models.BooleanField(default=True)
class Meta:
ordering = ['price']
def __str__(self):
return self.title
On the detailed view of the Product, I want to display the price of all ExternalProduct related to the Product. It works all fine with this view
# products/views.py
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
and this template
# product_detail.html
{% extends '_base.html' %}
{% block title %}{{ product.title }}{% endblock title %}
{% block content %}
<div class="book-detail">
<h2>{{ product.get_active_external_products }}</h2>
</div>
<div>
<ul>
{% for ep in product.external_products.all %}
<li>{{ ep.price }}</li>
{% endfor %}
</ul>
</div>
{% endblock content %}
The problem is that I just want to display the price of ExternalProduct which has active=True
Been trying to solve it with a custom method in products views.py
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_active_external_products(self):
return self.external_products.objects.filter(active=True)
And a modified for loop inside product_detail.html
{% for ep in product.get_active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}
But unfortunately without any success. The site does not crash, just doesn't show anything other than the rest of the html file.
Any advice?
You can use prefetch_related with Prefetch to filter only active external products. For this you need to override get_queryset() method in your view:
from django.db.models import Prefetch
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_queryset(self):
return Product.objects.prefetch_related(Prefetch("external_products", queryset=ExternalProduct.objects.filter(active=True), to_attr="active_external_products"))
Note to_attr="active_external_products" part. It tells that active external products will be available as active_external_products attribute. So in template you can get them like this:
{% for ep in product.active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}
Or you can just override get_context_data() to insert external products to the context directly:
class ProductDetailView(DetailView):
model = Product
context_object_name = 'product'
template_name = 'products/product_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(****kwargs)
context["active_external_products"] = self.object.external_products.filter(active=True)
return context
In this case in template you can use active_external_products variable name directly:
{% for ep in active_external_products %}
<li>{{ ep.price }}</li>
{% endfor %}
Models.py
class Experience(models.Model):
user = models.ForeignKey(User, null=True, on_delete = models.CASCADE)
company_name = models.CharField(max_length=100)
company_address = models.CharField(max_length=200)
post_held = models.CharField(max_length=30)
year_from = models.CharField(max_length=20)
year_to = models.CharField(max_length=20)
info = models.TextField()
def get_absolute_url(self):
return reverse('resume')
def __str__ (self):
return self.company_name
Views.py
def Resume(request):
experience = Experience.objects.filter(user = request.user)
return render(request, 'resume.html', {'experience': experience})
Template
<ul>
{% for an_experience in experience %}
<li ><h5>{{ an_experience }},</h5> {{ an_experience.company_address }} - {{ an_experience.post_held }}</li>
<small>{{ an_experience.year_from }} - {{ an_experience.year_to }}</small>
<div>
{{ an_experience.info }}
</div>
{% endfor %}
</ul>
if I use .all(), it is working perfectly but while trying to filter it using request.user nothing is displaying in the template file.
how can I retrieve the lesson modules by its parent lesson strictly? e.g if I change to the next lesson where at the same contains a lessonmodule /lesson1/task1 this one shouldn't display since it doesn't belong to lesson2? how can I fix this by only retrieve slugs, content by the lesson attach to?
views
class LessonDetailView(LoginRequiredMixin, View):
login_url = "/account/login/"
def get(self, request, course_slug, lesson_slug, *args, **kwargs):
lesson = get_object_or_404(Lesson.objects.select_related('course'), slug=lesson_slug, course__slug=course_slug)
lessonmodule = get_object_or_404(LessonModule.objects.select_related('lesson'), slug='hello_world')
context = {
'object': lessonmodule,
'previous_lesson': lesson.get_previous_by_created_at,
'next_lesson': lesson.get_next_by_created_at,
}
return render(request, "courses/lesson_detail.html", context)
models
class Lesson(models.Model):
title = models.CharField(max_length=120)
slug = models.SlugField(max_length=25,unique=True)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('lesson-detail',
kwargs={
'course_slug': self.course.slug,
'lesson_slug': self.slug
})
class LessonModule(models.Model):
slug = models.SlugField(unique=True, blank=True, null=True)
content = models.TextField()
lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE)
allowed_memberships = models.ManyToManyField(Membership)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, blank=True)
def __str__(self):
return self.slug
template
{% block content %}
<div class='container'>
<h1>Lesson Detail View</h1>
<div class='row'>
<div class='col-sm-6 col-md-6'>
{% if object is not None %}
<h3>{{ object.title }}</h3>
<p>{{ object.course.description }}</p>
{{object}}
{% if previous_lesson %}
Back
{% endif %}
{% if next_lesson %}
Next
{% endif %}
{% else %}
<h3>Upgrade membership</h3>
<p>To view this course you'll need to upgrade your membership</p>
<br>
<hr>
{% endif %}
</div>
</div>
</div>
{% endblock content %}
As I can see here, you can have multiple LessonModule for a single Lesson due to one-to-many relation between Lesson and LessonModule(ForeignKey relation basically). You can fetch all the LessonModules like this(using reverse query):
# view
def get(self, request, course_slug, lesson_slug, *args, **kwargs):
lesson = get_object_or_404(Lesson.objects.select_related('course').prefetch_related('lessonmodule_set'), slug=lesson_slug, course__slug=course_slug)
context = {
'object': lesson,
'previous_lesson': lesson.get_previous_by_created_at,
'next_lesson': lesson.get_next_by_created_at,
}
return render(request, "courses/lesson_detail.html", context)
# template
{% for lesson_module in object.lessonmodule_set.all %}
{{ lesson_module.slug }}
{% endfor %}
I am just sending the Lesson object to template, and using reverse query in template to get LessonModule objects.
I have category and product models. These have a many-to-many relation.
My models
class ProductCategories(models.Model):
name = models.CharField(max_length = 60)
image = models.ImageField(upload_to = 'ProductCategories')
publish_date = models.DateTimeField(auto_now=False, auto_now_add=True)
is_available = models.BooleanField()
class Product(models.Model):
category = models.ManyToManyField(ProductCategories)
name = models.CharField(max_length = 60)
price = models.DecimalField(max_digits=65, decimal_places=2)
description = models.TextField()
options = models.TextField()
tags = models.TextField()
publish_date = models.DateTimeField(auto_now=False, auto_now_add=True)
stock_number = models.IntegerField()
is_available = models.BooleanField()
My view
def category(request):
categories = ProductCategories.objects.all()
products = Product.objects.none()
for category in categories:
products = products.union(Product.objects.filter(category = category)[:4])
return render(request, 'shop/shopping.html', {'categories' : categories, 'products' : products})
My Html
{% for category in categories %}
<div class="row">
<h3 style="padding-left: 15px; padding-bottom: 15px">{% filter upper %}{{ category.name }}{% endfilter %}</h3>
</div>
<div class="row">
{% for product in products %}
{{ product.category }}
{% endfor %}
</div>
{% endfor %}
I would like to list categories. Under each category, 4 products will be listed.
is it possible to pass queryset which includes both products and their categories?
Thanks,
You can simply do:
def category(request):
categories = ProductCategories.objects.all()
return render(request, 'shop/shopping.html', {'categories' : categories})
And the template
{% for category in categories %}
{% for product in category.product_set.all|slice:":4" %}
{{ product.name }}
{% endfor %}
{% endfor %}
I have 2 models: UserProfile and User_Details and I want to display the details for one user.I think the problem is at the filter and I don't know how to filter after a string,I'm new to django.How can I fix this?
class ShowProfile(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'profile.html'
def get(self, request, pk, format=None):
details=User_Details.objects.all().filter(user=request.user.username)
serializer = ProfileSerializer2(details, many=True)
pprint.pprint(json.loads(JSONRenderer().render(serializer.data)))
return Response({'fields ': serializer})
def post(self, request):
serializer = CreatePostSerializer(data=request.data)
if not serializer.is_valid():
return Response({'fields': serializer })
user = UserProfile.objects.filter(username=request.user.username).first()
serializer.save(user=user)
pprint.pprint(json.loads(JSONRenderer().render(serializer.data)))
return redirect('mainPage')
Models:
class UserProfile(AbstractUser):
pass
class User_Details(models.Model):
user = models.OneToOneField(UserProfile, on_delete=models.CASCADE)
first_name = models.CharField(max_length=50, blank = True)
last_name = models.CharField(max_length=50, blank=True)
gender = models.CharField(max_length=6, blank=True, default='')
public_info=models.CharField(max_length=100, blank=True, default='')
Template:
{% extends 'base2.html' %}
{% load rest_framework %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-3 ">
<div class="list-group ">
Profile
My Posts
</div>
</div>
{% for d in fields %}
{{d.firs_name }}<h1>a</h1>
{% endfor %}
{% endblock %}