Django - Giving a view access to multiple models - django

I followed a tutorial on Youtube that makes a website to create posts. Currently, I am working on displaying other people's profiles and their posts on their profile pages. I did it by accessing the author of the first post, but it only works if the user posted something. Is there another way to do it?
The view:
class UserProfileView(ListView):
model = Post
template_name = 'blog/user_profile.html' # defaults to <app>/<model>_<viewtype>.html--> blog/post_list.html
context_object_name = 'posts' #defaults to objectlist
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author=user).order_by('-date_posted')
user_profile.html:
{% extends 'blog/base.html' %}
{% block title %}{{ view.kwargs.username }}'s Profile{% endblock title %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ posts.first.author.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ view.kwargs.username }}</h2>
<p class="text-secondary">{{ posts.first.author.email }}</p>
<p class="article-content">{{ posts.first.author.profile.bio }}</p>
</div>
</div>
{% if posts.first.author == user %}
<a class="btn btn-outline-secondary ml-2" href="{% url 'change_profile' %}">Edit Profile</a>
{% endif %}
</div>
<br>
{% if posts.first.author == user %}
<h3 class="ml-2 article-title">Blogs You Posted</h3>
{% else %}
<h3 class="ml-2 article-title">Blogs By {{ view.kwargs.username }}</h3>
{% endif %}
And the Post and Profile module:
class Post(models.Model):
title = models.CharField(max_length=50)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self) -> str:
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk':self.pk})
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
bio = models.CharField(max_length=225, blank=True, default='')
def __str__(self) -> str:
return f"{self.user.username}'s Profile"
def save(self, *args, **kwargs):
super(Profile, self).save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
img.thumbnail((300,300))
img.save(self.image.path)

First off, you probably don't need .first in your template, because you are using the first item from the QuerySet. If you are accessing a User profile, you can use the DetailView. In addition, you can try from a different page and provide filtered posts in context. Something like this, for example.
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['posts'] = Post.objects.filter(author=user) # from kwargs
return context
#template
<ul>
{% for post in posts %}
<li>{{ post.title }}</li>
<li>{{ post.content }}</li>
<li>{{ post.date_posted }}</li>
{% endfor %}
</ul>
PS Regards to Corey.

Related

Edit item within views in Django

There is some problem, I'm trying to update the product on the client by making changes and clicking on the update button - my page is refreshing w/o updating info, so the product has the same data as before. But in the logs, the status code of the GET request is 200 and shows the updated object in the database. When I try to update through the admin Django dashboard, everything works successfully, the problem is only on the client side of the web application. What issues can there be?
Thank you in advance!
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
CATEGORY = (
('Stationary', 'Stationary'),
('Electronics', 'Electronics'),
('Food', 'Food'),
)
class Product(models.Model):
name = models.CharField(max_length=100, null=True)
quantity = models.PositiveIntegerField(null=True)
category = models.CharField(max_length=50, choices=CATEGORY, null=True)
def __str__(self):
return f'{self.name}'
class Order(models.Model):
name = models.ForeignKey(Product, on_delete=models.CASCADE, null=True)
customer = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
order_quantity = models.PositiveIntegerField(null=True)
def __str__(self):
return f'{self.customer}-{self.name}'
views.py
#login_required(login_url='user-login')
#allowed_users(allowed_roles=['Admin'])
def product_edit(request, pk):
item = Product.objects.get(id=pk)
if request.method == 'POST':
form = ProductForm(request.POST, instance=item)
if form.is_valid():
form.save()
return redirect('dashboard-products')
else:
form = ProductForm(instance=item)
context = {
'form': form,
}
return render(request, 'dashboard/products_edit.html', context)
forms.py
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__'
html template:
{% extends 'partials/base.html' %}
{% block title %}Products Edit Page{% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row my-4">
<div class="col-md-6 offset-md-3 p-3 bg-white">
<h3>Edit Item</h3>
<hr>
<form>
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-info" type="submit" value="Confirm">
</form>
</div>
</div>
{% endblock %}
You have forgotten to pass POST method you are using GET.
{% extends 'partials/base.html' %}
{% block title %}Products Edit Page{% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row my-4">
<div class="col-md-6 offset-md-3 p-3 bg-white">
<h3>Edit Item</h3>
<hr>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-info" type="submit" value="Confirm">
</form>
</div>
</div>
{% endblock %}

Django Views has an issue not allowing all Context Variables to appear in template

I have made a Slider Model which contains 3 images and Texts related to it to slide in a Carousel
My issue is that I want these images to be shown as per the order assigned as per the model but only one appears.
If I remove the {% if sliders %} {% for slider in sliders %} the latest image only appears because in the views it is filtered by .latest('timestamp')
I have also tried to replace .latest('timestamp') in the views with .all() it still didn't work
This is the model:
class Slider(models.Model):
title = models.CharField(max_length=60)
image = models.ImageField(blank=False, upload_to='Marketing')
order = models.IntegerField(default=0)
header_text = models.CharField(max_length=120, null=True, blank=True)
middle_text = models.CharField(max_length=120, null=True, blank=True)
footer_text = models.CharField(max_length=120, null=True, blank=True)
button_text = models.CharField(max_length=120, null=True, blank=True)
active = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
class Meta:
ordering = ['order']
def __str__(self):
return self.title
def get_image_url(self):
return "%s/%s" % (settings.MEDIA_URL, self.image)
This is the view:
class HomeView(ListView):
model = Item
paginate_by = 10
template_name = "home.html"
def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
try:
context['marketing_message'] = MarketingMessage.objects.filter(
active=True).latest('timestamp')
except MarketingMessage.DoesNotExist:
context['marketing_message'] = None
try:
context['slider'] = Slider.objects.filter(
active=True).latest('timestamp')
except Slider.DoesNotExist:
context['slider'] = None
return context
This is the template:
{% if slider %}
{% for slider in sliders %}
<div class="carousel-item {% if forloop.first %} active {% endif %}">
<div class="view" style="background-image: url('{{ slider.get_image_url }}'); background-repeat: no-repeat; background-size: cover;">
<div class="mask rgba-black-strong d-flex justify-content-center align-items-center">
<div class="text-center white-text mx-5 wow fadeIn">
<h1 class="mb-4">
{% if slider.header_text %}
<strong>{{ slider.header_text }}</strong>
{% endif %}
</h1>
<p>
{% if slider.middle_text %}
<strong>{{ slider.middle_text }}</strong>
{% endif %}
</p>
<p class="mb-4 d-none d-md-block">
{% if slider.footer_text %}
<strong>{{ slider.footer_text }}
</strong>
{% endif %}
</p>
{% if slider.button_text %}
<a target="_blank" href="" class="btn btn-outline-white btn-lg">
{{ slider.button_text }}
<i class="fas fa-graduation-cap ml-2"></i>
</a>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
make following changes
views.py
class HomeView(ListView):
model = Item
paginate_by = 10
template_name = "home.html"
def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
try:
context['marketing_message'] = MarketingMessage.objects.filter(active=True).latest('timestamp')
except MarketingMessage.DoesNotExist:
context['marketing_message'] = None
try:
context['sliders'] = Slider.objects.filter(active=True).order_by('timestamp')
except Slider.DoesNotExist:
context['sliders'] = None
return context
template
{% if sliders %}
{% for slider in sliders %}
#Your html code
{% endfor %}
{% endif %}
You have declared context['slider'] in views.py whereas, you call it sliders in your templates.
Now you will not be able to loop through because that calls one object only so do the following changes in views.py:
context['slider'] = Slider.objects.filter(active=True).latest('timestamp')
to
context['slider'] = Slider.objects.filter(active=True)

In django, getting the following usernames in user profile

i'm building a following model. I have created a following model as shown below and it is working fine in the admin. I did not create a view for the following because i'm in primary stage of building i'm testing in templates to bring that usernames in userspostlist, but it's not working.
my models.py for following:
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='owner', on_delete=models.CASCADE)
following = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='followed_by')
def __str__(self):
return str(self.following.all().count())
class post(models.Model):
parent = models.ForeignKey("self", on_delete=models.CASCADE, blank=True, null=True)
title = models.CharField(max_length=100)
image = models.ImageField(upload_to='post_pics', null=True, blank=True)
video = models.FileField(upload_to='post_videos', null=True, blank=True)
content = models.TextField()
likes = models.ManyToManyField(User, related_name='likes', blank=True)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
objects = postManager()
def __str__(self):
return self.title
my views.py for userpostlist:
class UserPostListView(ListView):
model = post
template_name = 'blog/user_post.html' #<app>/<model>_<viewtype>.html
context_object_name = 'posts'
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return post.objects.filter(author=user).order_by('-date_posted')
and my userpostlist.html:
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<h1 class="mb-3">posts by {{view.kwargs.username}}</h1>
{% for user in username.owner.following.all %}
{{ user.username }}
{% endfor %}
{% for post in posts %}
<article class="content-section">
<div class="article-metadata">
<div class="img">
<img class="rounded-circle article-img" data-toggle="modal" data-target="#mymodal" src="{{ post.author.profile.image.url }}">
</div>
<div class="profile-info">
<a class="h2" href="{% url 'user-posts' post.author.username %}">{{ post.author }}</a>
<div class="text-muted">{{ post.date_posted }}</div>
</div>
</div>
{% endfor %}
{% endblock content %}
Is there any problem with the userpostlist view in views.py? do i need to add some code there?
You have some options here, where one is to pass users into context:
class UserPostListView(ListView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['author'] = get_object_or_404(User, username=self.kwargs.get('username')
return context
And then instead of this:
{% for user in username.owner.following.all %}
{{ user.username }}
{% endfor %}
Use this:
{{ author.username }}

Reverse Query Through Django Template File

models.py file
class Company(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Contact(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
def __str__(self):
return self.first_name
views.py file
class company_detail(DetailView):
def get(self, request, *args, **kwargs):
company = get_object_or_404(Company, pk=kwargs['pk'])
context = {'company': company}
return render(request, 'crm/company_detail.html', context)
company_detail.html file
{% extends 'base.html' %}
{% block content %}
<div id="container">
<ul>
<li>{{company.name}}</li>
</ul>
{% for company in object_list %}
{{ company.name }}
{% for Contact in company.contact_set.all %}
{{Contact.first_name}}
{% empty %}
<!-- no entries -->
{% endfor %}
{% endfor %}
</div>
{% endblock content %}
I'm trying to get the Contacts who are under that company to show up on the company_detail.html page. How do I reverse query it properly to show all Contacts under that company?
Thanks in advance
There is no object_list in your DetailView's context, only the object. You need to remove the for loop over object_list in the template
{% for contact in company.contact_set.all %}
{{ contact.first_name }}
{% empty %}
<!-- no entries -->
{% endfor %}

Django unrecognized token: "#" while query

I simply want to implement a search view into my Django app. But when i try to search something on my App i get the following error:
'unrecognized token: "#"'
In the end i want that my Query is a combination of category and searchword. So that the user can filter specific categories (Just like Amazon.com searchfield) e.g.: http://127.0.0.1:8000/search/?category=1&q=hallo
base.html
...
<div class="globalsearch">
<form id="searchform" action="{% url 'search' %}" method="get" accept-charset="utf-8">
<label for="{{ categorysearch_form.category.id_for_label }}">In category: </label> {{ categorysearch_form.category }}
<input class="searchfield" id="searchbox" name="q" type="text" placeholder="Search for ...">
<button class="searchbutton" type="submit">
<i class="fa fa-search"></i>
</button>
</form>
</div>
</div>
...
categorysearch_form is a dropdown selector that gets his ID from the Database.
views.py
...
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
from django.views.generic import ListView
class globalsearch(ListView):
"""
Display a Post List page filtered by the search query.
"""
model = Post
paginate_by = 10
def get_queryset(self):
qs = Post.objects.all()
keywords = self.request.GET.get('q')
if keywords:
query = SearchQuery(keywords)
title_vector = SearchVector('title', weight='A')
content_vector = SearchVector('content', weight='B')
tag_vector = SearchVector('tag', weight='C')
vectors = title_vector + content_vector + tag_vector
qs = qs.annotate(search=vectors).filter(search=query)
qs = qs.annotate(rank=SearchRank(vectors, query)).order_by('-rank')
return qs
...
urls.py
...
url(r'^search/$', views.globalsearch.as_view(), name='search'),
...
Search.html results are getting displayd here:
{% extends 'quickblog/base.html' %}
{% block content %}
{% for post in object_list %}
<div class="post">
<h1><u>{{ post.title }}</u></h1>
<p>{{ post.content|linebreaksbr }}</p>
<div class="date">
<a>Published by: {{ post.author }}</a><br>
<a>Published at: {{ post.published_date }}</a><br>
<a>Category: {{ post.category }}</a><br>
<a>Tag(s): {{ post.tag }}</a>
</div>
</div>
{% endfor %}
Post Model
...
#Post Model
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=75)
content = models.TextField(max_length=10000)
tag = models.CharField(max_length=50, blank=True)
category = models.ForeignKey(Category, verbose_name="Category", on_delete=models.CASCADE, null=True)
postattachment = fields.FileField(upload_to='postattachment/%Y/%m/%d/', blank=True ,null=True)
postcover = fields.ImageField(upload_to='postcover/%Y/%m/%d/', null=True, dependencies=[
FileDependency(processor=ImageProcessor(
format='JPEG', scale={'max_width': 200, 'max_height': 200}))
])
created_date = models.DateField(auto_now_add=True)
published_date = models.DateField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
class Meta:
ordering = ["-title"]
def __str__(self):
return self.title
...
i guess im simply missing something
at that point...
I already read that this issue comes from local SQL database aka. SQLite, could that be true?
Smb. has a solution?
Thanks
After a bit of tinkering with a friend i found a solution, the Django Documentation according to vector searching is really not that good...
views.py
class globalsearch(ListView):
"""
Display a Post List page filtered by the search query.
"""
model = Post
paginate_by = 10
template_name = 'quickblog/search.html'
def get_queryset(self):
keywords = self.request.GET.get('q')
if keywords:
query = SearchQuery(keywords)
title_vector = SearchVector('title', weight='A')
content_vector = SearchVector('content', weight='B')
tag_vector = SearchVector('tag', weight='C')
vectors = title_vector + content_vector + tag_vector
qs = Post.objects.annotate(rank=SearchRank(vectors, query)).filter(rank__gte=0.1).order_by('-rank')
return qs
models.py
#Post Model
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=75)
content = models.TextField(max_length=10000)
tag = models.CharField(max_length=50, blank=True)
category = models.ForeignKey(Category, verbose_name="Category", on_delete=models.CASCADE, null=True)
postattachment = fields.FileField(upload_to='postattachment/%Y/%m/%d/', blank=True ,null=True)
postcover = fields.ImageField(upload_to='postcover/%Y/%m/%d/', null=True, dependencies=[
FileDependency(processor=ImageProcessor(
format='JPEG', scale={'max_width': 200, 'max_height': 200}))
])
created_date = models.DateField(auto_now_add=True)
published_date = models.DateField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
class Meta:
ordering = ["-title"]
def __str__(self):
return self.title
search.html
{% extends 'quickblog/base.html' %}
{% block content %}
{% for post in object_list %}
<div class="post">
<h1><u>{{ post.title }}</u></h1>
<p>{{ post.content|linebreaksbr }}</p>
<div class="date">
<a>Published by: {{ post.author }}</a><br>
<a>Published at: {{ post.published_date }}</a><br>
<a>Category: {{ post.category }}</a><br>
<a>Tag(s): {{ post.tag }}</a>
</div>
</div>
{% endfor %}
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
{% endblock %}
urls.py
url(r'^search/$', views.globalsearch.as_view(), name='search'),
Hope that i was able to help the next one with that Post :D