Django - Retrieve Data for 2 Separate Querysets On One Page - python

I am using Django, PyCharm 2018.3.7, Python 3.7, and Postgres.
Unfortunately, I do not understand how to display on one screenpage (Django form) the results for 2 separate querysets that need the same ID passed by a dynamic filter request (GET). There is a table/model (ASSIGNMENTS) with a One-To-Many relationship to another table/model (PRODUCTS). The ID being passed is the ASSIGNMENTS.ID, which is the PK of ASSIGNMENTS and the FK of PRODUCTS.
I am not trying to join these 2 querysets. I need the user to see the information for the ASSIGNMENT first and then below I want them to see all of the PRODUCTS that belong to that ASSIGNMENT.
I do not need to update/create data, so no POST is needed. Only want to retrieve, GET, data that exists.
Below are the code for models.py, views.py, and templates. It works perfect with just ASSIGNMENTS.
MODELS.PY
class Assignments(models.Model):
id = models.DecimalField(db_column='ID', primary_key=True, max_digits=9, decimal_places=0)
name = models.CharField(db_column='NAME', unique=True, max_length=40)
def __str__(self):
return self.name + ' ' + '(' + '#' + str(self.id) + ')'
class Meta:
ordering = 'name',
db_table = 'ASSIGNMENTS'
class Products(models.Model):
id = models.DecimalField(db_column='ID', primary_key=True, max_digits=11, decimal_places=0)
assignment = models.ForeignKey(Assignments, models.DO_NOTHING, related_name='productsfor_assignment', db_column='ASSIGNMENT_ID', blank=True, null=True)
name = models.CharField(db_column='NAME', max_length=80)
def __str__(self):
return self.name + ' ' + '(' + '#' + str(self.id) + ')'
class Meta:
ordering = 'name',
db_table = 'PRODUCTS'
VIEWS.PY
def search_form(request):
return render(request, 'assignments/search_form.html')
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
assign = Assignments.objects.filter(id__icontains=q)
return render(request, 'assignments/search_results.html',
{'AssignmentsResults': assign, 'query': q})
else:
# if the query is empty, render the 'search_form html' template again.
# display an error message in that template. So, pass a template variable.
# 'search_form html' will check for the 'error' variable
return render(request, 'assignments/search_form.html', {'error': True})
TEMPLATE 'search_form.html'
{% extends "base.html" %}
{% block content %}
<br><br>
<h1> Assignment ID Search Form </h1>
<br><br>
{% if error %}
<p style="color: red;">Please submit a search term.</p>
{% endif %}
<form action="/search/" method="get">
<input type="text" name="q" placeholder="Enter ID here ...">
<input type="submit" value="Search">
</form>
<p>
Page last updated: {{ last_updated|date:'D d F Y' }}
</p>
{% endblock content %}
TEMPLATE 'search_results.html'
{% extends "base.html" %}
{% block content %}
<br><br>
<h1> Assignment ID Search Results</h1>
<p>You searched for: <strong>{{ query }}</strong></p>
{% if AssignmentsResults %}
<ul>
{% for assignments in AssignmentsResults %}
<li>
{{ assignments.id }}, {{ assignments.name }}
</li>
{% endfor %}
</ul>
{% else %}
<p>No assignments matched your search criteria.</p>
{% endif %}
{% endblock content %}
This is what I finally put together to include Products (shown below in the failed views.py and template). But, it does not work.
This was the FAILED VIEWS.PY
def search_form(request):
return render(request, 'assignments/search_form.html')
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
assign = Assignments.objects.filter(id__icontains=q)
prod = Products.objects.filter(assignment__icontains=q)
return render(request, 'assignments/search_results.html',
{'AssignmentsResults': assign, 'query': q})
return render(request, 'assignments/search_results.html',
'ProductsResults': prod, 'query': q)
else:
# if the query is empty, render the 'search_form html' template again.
# display an error message in that template. So, pass a template variable.
# 'search_form html' will check for the 'error' variable
return render(request, 'assignments/search_form.html', {'error': True})
This was the FAILED TEMPLATE CODE 'search_results.html
{% extends "base.html" %}
{% block content %}
<br><br>
<h1> Assignment ID Search Results</h1>
<p>You searched for: <strong>{{ query }}</strong></p>
{% if AssignmentsResults %}
<ul>
{% for assignments in AssignmentsResults %}
<li>
{{ assignments.id }}, {{ assignments.name }}
</li>
{% endfor %}
</ul>
<p> These are the Products attached to the Assignment.</p>
{% if ProductsResults %}
<ul>
{% for products in ProductsResults %}
<li>
{{ products.assignment }}, {{ products.name }}
</li>
{% endfor %}
</ul>
{% else %}
<p> No products for this assignment exist.</p>
{% endif %}
{% else %}
<p>No assignments matched your search criteria.</p>
{% endif %}
{% endblock content %}
I have created the project by following 'Build a website with Django 2, updated for Django 2.2' by Nigel George. I have looked at several youtube instructional videos and read questions on stackoverflow. (These are only a couple.)
How to combine two or more querysets in a Django view?
Displaying a series of inline forms for a queryset of Django models
Django: Add queryset to inlineformsets
Django nested QuerySets
InlineFormSet with queryset of different model
However, none of them have been able to help me with my problem of presenting 2 separate queryset results on one form.
I'd certainly appreciate any suggestions and help provided.
Thanks

Okay, let's see. If you just want to display the information that it seems to be intended in search_results.html, I suggest you to refactor a little bit your query as follows.
(I will try to clean out the weird things (for example having two consecutive return statements) as much as I can to illustrate my point)
views.py:
from django.shortcuts import render, redirect
from django.urls import reverse
def search(request):
q = request.GET.get('q', None)
if q:
assignments_qs = Assignments.objects.filter(id__icontains=q) \
.prefetch_related('productsfor_assignment')
context_data = {'assignments': assignments_qs}
return render(request, 'assignments/search_results.html', context_data)
else:
return redirect(reverse('search_form', kwargs={'error': True}))
def search_form(request, error=False):
return render(request, 'assignments/search_form.html', {'error': error})
search_results.html
{% if assignments %}
<ul>
{% for assignment in assignments %}
<li>
{{ assignment.id }}, {{ assignment.name }}
</li>
{% if assignment.productsfor_assignment.exists %}
<p> These are the Products attached to the Assignment.</p>
<ul>
{% for product in assignment.productsfor_assignment.all %}
<li>
{{ product.assignment }}, {{ product.name }}
</li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
</ul>
{% else %}
<p>No assignments matched your search criteria.</p>
{% endif %}
I think this is the best and the most similar way to achieve what you want considering what you currently have.

Related

Django forloop inside forloop in Template

hello iam trying to make Dropdown Menu in Navbar with querysets. I was trying to make it with two querysets send to html template ( "stages","questions") that are related to each other by key (id , stage_id), but i cant figured out why forloops cant work together. My second try was with passing data in json to javascript and make it with js QuerySelector, but django querysets are not JSON serializable. Any suggestions how to make it please ?
views.py
def edit_pages(request, gameid):
stages = Stage.objects.filter(game_id=gameid)
print(stages)
questions = []
for st in stages:
questions = chain(questions,Question.objects.filter(stage_id=st.id))
print(questions)
return render(request, "homeSuperuser/edit_pages.html",{'stages': stages, 'questions': questions})
html
<body>
<div class="topnav">
{% for st in stages %}
<div class="dropdown">
<button class="dropbtn">{{st.stage_name}}</button>
<div class="dropdown-content">
{% for qs in questions %}
{% if qs.stage_id == st.id %}
{{qs.question_name}}
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</body>
Define a model method as follows
class Stage(models.Model):
name = models.CharField(max_length=128)
def get_questions(self):
return Question.objects.filter(stage=self)
def __str__(self):
return str(self.name)
class Question(models.Model):
stage = models.ForeignKey(Stage, on_delete=models.PROTECT, related_name="questions")
name = models.CharField(max_length=128)
def __str__(self):
return str(self.name)
Now you can loop them in the template as follows
{% for st in stages %}
<div class="dropdown">
<button class="dropbtn">{{st.name}}</button>
<div class="dropdown-content">
{% for qs in st.get_questions %}
{{qs.name}}
{% endfor %}
</div>
</div>
{% endfor %}

Django Please help me place a checkbox and a mail box with a send button on the html page

One of functionality in my training project:
subscribe to the news by check-box and e-mail.
Send newsletter daily.
The user can unsubscribe from the mailing list in his profile by unchecking the checkbox.
It so happened that first I set up a daily newsletter for users who have booleanfield = true.
For it I marked the checkboxes in the admin panel. It works.
Now it is necessary to add the checkbox and the mail field to the news page.
I'm stuck on the simplest. Tired and confused.
Please help me place a checkbox and a mail box with a send button on the news page
models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
hr = models.BooleanField(default=False)
subscribed_for_mailings = models.BooleanField(default=False)
subscription_email = models.EmailField(default="")
def __str__(self):
return str(self.user)
Forms.py
class MailingForm(forms.ModelForm):
class Meta:
model = models.Profile
fields = ('subscription_email', 'subscribed_for_mailings', )
widgets = {
'subscription_email': forms.EmailInput(attrs={"placeholder": "Your Email..."}),
'subscribed_for_mailings': forms.CheckboxInput,
}
views.py
def all_news(request):
today = date.today()
today_news = models.TopNews.objects.filter(created__gte=today)
return render(request, "news.html",
{'today_news': today_news})
def mailing_news(request):
if request.method == 'POST':
mailing_form = forms.MailingForm(request.POST)
if mailing_form.is_valid():
mailing_form.save()
return HttpResponse('You will receive news by mail')
else:
mailing_form = forms.MailingForm()
return render(request, "news.html", {'mailing_form': mailing_form})
urls.py
...
path('news/', views.all_news, name='all_news'),
...
news.html
{% extends 'base.html' %}
{% block title %}
News
{% endblock %}
{% block body %}
<h1>Last news</h1>
{% for news in today_news%}
<h3>{{ news.title }}</h3>
Read this news
<p>
{{ news.created }}
</p>
<hr>
{% endfor %}
<h4>I want to receive news by mail</h4>
<form action="." method="post">
{{ mailing_form.as_p }}
{% csrf_token %}
<label>
<input type="submit" value="Subscribe">
</label>
</form>
{% endblock %}
The page displays a list of news and only the "send" button. There is no check-box and a field for mail
enter image description here
Finally I realized this functionality in a different way:
forms.py
class MailingForm(forms.ModelForm):
class Meta:
model = models.Profile
fields = ('subscribed_for_mailings', 'subscription_email', )
views.py
#login_required
def mailing_news(request):
if request.method == "POST":
mailing_form = forms.MailingForm(request.POST,
instance=request.user.profile,
)
if mailing_form.is_valid():
mailing_news = mailing_form.save(commit=False)
mailing_news.subscribed_for_mailings = mailing_news.subscribed_for_mailings
mailing_news.subscription_email = mailing_news.subscription_email
mailing_news.save()
return render(request, "subscribe_complete.html",
{"mailing_news": mailing_news})
else:
mailing_form = forms.MailingForm()
return render(request, 'subscribe.html', {"mailing_form": mailing_form})
news.html
{% extends 'base.html' %}
{% block title %}
News
{% endblock %}
{% block body %}
<h1>Last news</h1> {{ news.created }}
{% for news in today_news%}
<h3>{{ news.title }}</h3>
Read this news
<hr>
{% endfor %}
I want to receive news by mail
{% endblock %}
urls.py
...
path('subscribe/', views.mailing_news, name='subscribe')
...
news.html
{% extends 'base.html' %}
{% block title %}
News
{% endblock %}
{% block body %}
<h1>Last news</h1> {{ news.created }}
{% for news in today_news%}
<h3>{{ news.title }}</h3>
Read this news
<hr>
{% endfor %}
I want to receive news by mail
{% endblock %}
subscribe.html
{% extends 'base.html' %}
{% block title %}
Subscribe
{% endblock %}
{% block body %}
<form action="." method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ mailing_news.as_p }}
{% if user.profile.subscribed_for_mailings is True %}
<input type="checkbox" name="subscribed_for_mailings" id="id_subscribed_for_mailings" checked="">
If you don't want to receive emails anymore, uncheck
<br>
Subscription email: <input type="email" name="subscription_email" value={{ user.profile.subscription_email }} class="vTextField" maxlength="254" id="id_subscription_email">
{% else %}
<label>
<input type="checkbox" name="subscribed_for_mailings" id="id_subscribed_for_mailings">
I want to subscribe for mailing news
</label>
<p><label>
Send news on my email:
<input type="email" name="subscription_email" class="vTextField" maxlength="254" id="id_subscription_email">
</label></p>
{% endif %}
<p><input type="submit" value="Update"></p>
</form>
{% endblock %}
subscribe_complete.html
{% extends 'base.html' %}
{% block title %}
Subscribing complete
{% endblock %}
{% block body %}
<h3>Hi {{ user.username }}</h3>
Thanks for subscribing.
You will receive daily news by email: {{ user.profile.subscription_email }}
{% endblock %}
you need to change subscribed_for_mailings in mailing news, like this
def mailing_news(request):
if request.method == 'POST':
mailing_form = forms.MailingForm(request.POST)
if mailing_form.is_valid():
profile = mailing_form.save(commit=False) ####
profile.subscribed_for_mailings = mailing_form.cleaned_data.get('subscribed_for_mailings') ####
profile.subscription_email = mailing_form.cleaned_data.get('subscription_email') ####
profile.save() #### new_line
return HttpResponse('You will receive news by mail')
else:
mailing_form = forms.MailingForm()
return render(request, "news.html", {'mailing_form': mailing_form})
you can change in cleaned_data.get('....')

ForeignKey don't show data

When I try to use foreignKey, html don't response
Models.py
class Home(models.Model):
book = models.ForeignKey(Book, verbose_name=(
"book"), on_delete=models.CASCADE, related_name="book")
Views.py
def main(request):
posts = Home.objects.all()
return render(request, 'home/home-page.html', {'posts': posts})
HTML
{% for post in posts %}
{% for book in post.book.all %}
{{ book }}
{% endfor %}
{% endfor %}
What going wrong?
all doesn't make any sense here, with a one to many relationship home only has one book, a book has many homes
so the inner for loop isn't needed, you can just use post.book
{% for post in posts %}
{{ post.book }}
{% endfor %}

django-filter checkbox submit

I am using django-filter==2.1.0 for implementing search filter. I already implement it. But now i need to search by clicking checkbox not by search button. My codes are given below:
models.py
class Book(models.Model):
name = models.CharField(max_length=100)
publication = models.ForeignKey(Publication, on_delete=models.CASCADE)
filters.py
class BookFilter(django_filters.FilterSet):
publication = django_filters.ModelMultipleChoiceFilter(queryset=Publication.objects.all(),
widget= forms.CheckboxSelectMultiple)
class Meta:
model = Book
fields = ['publication']
views.py
def test_view(request):
book_list = Book.objects.all()
book_filter = BookFilter(request.GET, queryset=book_list)
temp = book_filter.form
return render(request, 'test.html', {'filter': book_filter})
template
{% extends 'base.html' %}
{% load widget_tweaks %}
{% block content %}
<form method="get">
{% for choice in filter.form.publication %}
<label>
{{ choice.tag }}
<span class="max-content-width">{{ choice.choice_label }}</span>
</label>
{% endfor %}
<button type="submit">Search</button>
</form>
<ul>
{% for book in filter.qs %}
<li>{{ book.name }}</li>
{% endfor %}
</ul>
{% endblock %}
It's working properly. But i want to add widget = forms.CheckboxInput(attrs={'onclick': 'this.form.submit();'}) in my filters.py for checkbox input. I don't understand how can i add another widget. Please help me to solve this problem.
Create a widget, write JavaScript script and use Media metaclass on widget to use it. After this just use your widget on your checkbox field.

How to set a predefined form value from a link in Django?

My project is laid out like so:
1. page
has many: categories
2. category
belongs to: page
has many: items
3. item
belongs to: category
when I enter a page I'd like to modify (add new categories or new items to those categories), so far I've only gotten to the point where I can add a new category or an item through a main link that gets me to a form where I have to chose which category the item must belong to. What I'd like to do is have a link "Add new item" next to categories title and when I click on it, the item form sets to that category by default. How can I do that? My current form looks very primitive, like so:
{% extends "base.html" %}
{% block content %}
{% if page.id %}
<h1>Edit Category</h1>
{% else %}
<h1>Add Category</h1>
{% endif %}
<form action="{{ action }}" method="POST">
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type="submit" id="save_page" value="Save" class="success button" /> Cancel
</form>
{% endblock %}
So, the way to do this is to exclude it from the form completely and set it in the view on save.
class ItemForm(forms.ModelForm):
class Meta:
model = Item
exclude = ('category',)
and the view:
def create_item(request, category_id):
if request.method == 'POST':
form = ItemForm(request.POST)
if form.is_valid():
item = form.save(commit=False)
item.category_id = category_id
item.save()
return redirect(...)
...etc...
which is served via a url:
url(r'^create_item/(?P<category_id>\d+)/$, 'create_item', name='create_item'),
and which you can therefore link to from your categories list:
{% for category in categories %}
<li>{{ category.name }} - Create item</li>
{% endfor %}

Categories