Django Filters on multiple models - python

I am a new django/python user and I am having an issue using django-filters with related models. I have created a tutorial application for myself with two models:
class Customer(models.Model):
def __str__(self):
return self.Customer_Name
Customer_Name = models.CharField(max_length=100)
SFDC_Customer_Record_Number = models.IntegerField(default='')
Zone = models.CharField(max_length=50, default='')
Government = models.BooleanField(default=False)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.DO_NOTHING,default=User)
class Vue_Quote(models.Model):
def __str__(self):
return self.Quote_Name
Quote_Name = models.CharField(max_length=100)
SFDC_Golden_Opp_ID = models.IntegerField()
Vue_System_Count = models.IntegerField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.DO_NOTHING,default=User)
Quote_Type = models.CharField(max_length=100)
Customer = models.ForeignKey(Customer , on_delete=models.DO_NOTHING, default='')
I have also created a filter.py file:
import django_filters
from .models import *
class CustomerFilter(django_filters.FilterSet):
class Meta:
model = Customer
fields = {
'Customer_Name': ['icontains'],
'Zone' : ['icontains'],
}
class QuoteFilter(django_filters.FilterSet):
class Meta:
model = Vue_Quote
fields = {
'Quote_Name': ['icontains'],
'author__username' : ['icontains'],
'Customer__Customer_Name' : ['icontains'],
}
In my templates I want to display the filter fields for quoteFilter and customerFilter (this is working). Then I have a sort of gallery/list that iterates through each customer and their respective quotes. My issue is that only my customerFilter works. I cannot find anyway to create nested for loops that can provide results for the user input across both model's fields.
Here is my current html which works for the customer filter but does not attempt to filter the quote content at all:
% extends "base.html" %}
{% load bootstrap %}
{% block content %}
<form method="get">
{{customerFilter.form|bootstrap}}
{{quoteFilter.form|bootstrap}}
<button class= "btn btn-primary" type="submit">Search</button>
</form>
{% if user.is_authenticated %}
<h2>Quote Explorer Gallery</h2>
<p></p>
{% for Customer in customerFilter.qs %}
{% for Quote in Customer.vue_quote_set.all %}
<HTML WITH CONTENT CONTINUES HERE>
Here is my view in case anyone needs to see it:
Customers = Customer.objects.all()
Quotes = Vue_Quote.objects.all().prefetch_related('Customer')
customerFilter = CustomerFilter(request.GET, queryset=Customers)
quoteFilter = QuoteFilter(request.GET, queryset=Quotes)
context = {
'Users': User.objects.all(),
'Customers': customerFilter.qs,
'Quotes': quoteFilter.qs,
'customerFilter' : customerFilter,
'quoteFilter' : quoteFilter,
}
print(context)
return render(request, 'home.html', context)
If anyone can advise how I can change my nested for loop, or how I can address the entire multi-model search differently I would be very grateful. Thank you.

Related

__in filter only returning one value, show query through intermediate table

Noob at coding and need help. I am trying to render the view article by filtering through the model Spots. I have an intermediate table ArticleSpots to link the 2 tables Spots and Articles. In the views article I want to show only the spots that are linked to that specific article. My problem is that Spots.objects.filter(id__in=articleSpots) only shows the first one value and not all of the spots that are linked. What am I doing wrong here?
views.py
def article(request, slug):
articles = get_object_or_404(Articles, slug=slug)
article_id = articles.id
articleSpots = ArticleSpots.objects.filter(article__id=article_id)
spots = Spots.objects.filter(id__in=articleSpots)
context = {"spots": spots, "articles": articles}
template_name = "articletemplate.html"
return render(request, template_name, context)
models.py
class ArticleSpots(models.Model):
article = models.ForeignKey('Articles', models.DO_NOTHING)
spot = models.ForeignKey('Spots', models.DO_NOTHING)
class Meta:
managed = True
db_table = 'article_spots'
verbose_name_plural = 'ArticleSpots'
def __str__(self):
return str(self.article) + ": " + str(self.spot)
class Articles(models.Model):
title = models.CharField(max_length=155)
metatitle = models.CharField(max_length=155)
slug = models.SlugField(unique=True, max_length=155)
summary = models.TextField(blank=True, null=True)
field_created = models.DateTimeField(db_column='_created', blank=True, null=True)
field_updated = models.DateTimeField(db_column='_updated', blank=True, null=True)
cover = models.ImageField(upload_to="cover", blank=True, default='logo-00-06.png')
class Meta:
managed = True
db_table = 'articles'
verbose_name_plural = 'Articles'
def __str__(self):
return str(self.id) + ": " + str(self.title)
class Spots(models.Model):
title = models.CharField(max_length=155)
metatitle = models.CharField(max_length=155)
slug = models.SlugField(unique=True, max_length=155)
author = models.ForeignKey(Authors, models.DO_NOTHING)
field_created = models.DateTimeField(db_column='_created', blank=True, null=True)
field_updated = models.DateTimeField(db_column='_updated', blank=True, null=True)
cover = models.ImageField(upload_to="cover", blank=True, default='logo-00-06.png')
summary = models.TextField(blank=True, null=True)
content1 = models.TextField(blank=True, null=True)
content2 = models.TextField(blank=True, null=True)
class Meta:
managed = True
db_table = 'spots'
verbose_name_plural = 'Spots'
def __str__(self):
return str(self.id) + ": " + str(self.title)
html
<!-- START MAIN -->
<main class="page"></main>
<p>
{{ spots.title }} <br />
{{ spots.content1 }} <br />
{{ articles.title }}
</p>
{% for spots in spots %} {{ spots.title}} {% endfor %}
<!-- END MAIN -->
You are currently retrieving Spots that have the same primary key as the ArticleSpots object, but that does not make much sense: it is possible that this is the case, but even if that happens, the returned Spots does not per se is linked to a relevant ArticleSpots with the given article.
You can retrieve the relevant Spots with:
def article(request, slug):
article = get_object_or_404(Articles, slug=slug)
spots = Spots.objects.filter(articlespots__article=article)
context = {'spots': spots, 'article': article}
return render(request, 'articletemplate.html', context)
I would strongly advise to name you Article object article since it is a single Article, not a collection of Articles. spots on the other hand is a collection of spots.
It makes no sense to render {{ spots.content1 }} and {{ spots.title }}, since spots is a collection of Spots that can contain zero, one or more items.
The template thus should look like:
<p>
{{ article.title }}
</p>
{% for spot in spots %} {{ spot.title}} {% endfor %}
Note: normally a Django model is given a singular name, so Articles instead of Article.

Check if user had filled questionnaire before with Django

I'm working on a questionnaire and I made a page where user have a list of which questionnaires to fill and which did he filled before but I have stucked.
I like to check if the user filled a form/questionnaire before and if he didn't show him the questionnaire link.
My solutions doesn't work because it checks the db just if the user filled the questionnaire but if he did not (no row for him in the db) it shows a blank cell in my table.
(I don't know if exists query could be a solution but I can't made it work)
main.html
{% for i in oke_vezetoi %}
{% if i.vezetoi_ok == True %}
<td><button class="btn btn-sm btn-outline-info"> Kitöltöm</button>
<td><i class="fas fa-running fa-2x text-dark"></i></td>
{% else %}
<td class="text-success text-uppercase">Kitöltötted</button>
<td><i class="fas fa-check fa-2x text-success"></i></td>
{% endif %}
{% endfor %}
views.py
def main(request):
oke_vezetoi = Vezetoi.objects.filter(user_name=request.user)
oke_stressz = Stressz_teszt.objects.filter(user_name=request.user)
context = {
'oke_vezetoi': oke_vezetoi,
'oke_stressz': oke_stressz,
}
return render(request, 'stressz/main.html', context)
models.py
class Vezetoi(models.Model):
def __str__(self):
return str(self.user_name)
user_name = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
vezetoi_v01 = models.IntegerField( null=True)
vezetoi_v02 = models.IntegerField( null=True)
vezetoi_v03 = models.IntegerField( null=True)
vezetoi_v04 = models.IntegerField( null=True)
vezetoi_v05 = models.IntegerField( null=True)
vezetoi_v06 = models.IntegerField( null=True)
vezetoi_v07 = models.IntegerField( null=True)
vezetoi_v08 = models.IntegerField( null=True)
vezetoi_v09 = models.IntegerField( null=True)
vezetoi_v10 = models.IntegerField( null=True)
vezetoi_v11 = models.IntegerField( null=True)
vezetoi_v12 = models.IntegerField( null=True)
vezetoi_ok = models.BooleanField()
forms.py
class VezetoiForm(forms.ModelForm):
class Meta:
model = Vezetoi
fields = ['vezetoi_v01', 'vezetoi_v02', 'vezetoi_v03', 'vezetoi_v04', 'vezetoi_v05', 'vezetoi_v06', 'vezetoi_v07', 'vezetoi_v08', 'vezetoi_v09', 'vezetoi_v10', 'vezetoi_v11', 'vezetoi_v12', 'vezetoi_ok' ]
I'm not sure if I'm interpreting your question correctly, but it seems like there are a few things that you could optimise.
It sounds like there should only be one entry per user in the Vezetoi model.
If this is true, you should enforce ForeignKey(unique=True), and you don't need a for loop and you can use Vezetoi.objects.get() in your views.py
If this is not true, and there are multiple Vezetoi integers per user, you might want to have one Vezetoi object for each integer.
If the user hasn't submitted a questionnaire, then the Vezetoi model object will not exist.
Since the object doesn't exist, it won't appear in the oke_vezetoi queryset, so the object attribute i.vezetoi_ok will not be found in your loop (this is why your table row is blank).
Assuming the field vezetoi_ok is only intended to check for the existence of the questionnaire, it can only ever be True, so you can remove it from the model definition.
If these are not true, I'll need to amend the answer and I'll ask you to provide more information about what these models are tracking, the content of the Stressz_teszt model and urls.py as well as how the VezetoiForm is implemented.
So in the case that I've described, I'd do it like this with a class based view and the get_context_data method.
models.py
class Vezetoi(models.Model):
def __str__(self):
return str(self.user_name)
user_name = models.ForeignKey(User, on_delete=models.CASCADE, unique=True)
vezetoi_v01 = models.IntegerField(null=True)
...
# vezetoi_ok = models.BooleanField()
forms.py
class VezetoiForm(forms.ModelForm):
class Meta:
model = Vezetoi
fields = '__all__'
views.py
class MainView(TemplateView):
template_name = 'main.html'
def get_context_data(self, **kwargs):
context = super().get_context_data()
context['oke_vezetoi'] = Vezetoi.objects.get(user_name=request.user)
context['oke_stressz'] = Stressz_teszt.objects.get(user_name=request.user)
return context
main.html
{% extends 'base.html' %}
{% if oke_vezetoi %}
{# do not display link #}
{% else %}
{# display link #}
{% endif %}

I want to get result of two query data using ORM in django

I want to get result of two query data using ORM in django but only one data is being displayed. How may i resolve this?
My codes:
views.py
def home(request):
codes, descrp = Major.objects.raw('SELECT p.major_cd, m.description FROM percentages p, major m WHERE p.major_cd = m.major_cd;')
context = {
"codes": codes,
"descrp": descrp
}
return render(request, "website/index.html" , context )
index.html
<select class="form-control select2">
<option>Select Major Head</option>
{% for cd, ds in codes, descrp %}
<option> {{ cd, ds }} </option>
{% endfor %}
</select>
Models
class Major(models.Model):
major_cd = models.IntegerField(primary_key=True)
description = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = False
db_table = 'major'
class Percentages(models.Model):
major_cd = models.IntegerField(primary_key=True)
percentage = models.CharField(max_length=5, blank=True, null=True)
class Meta:
managed = False
db_table = 'percentages'
I want to filter out 'major_cd' in major table based on 'major_cd' in percentages table.
Expected Results:
Data with column = major_cd, description from major table
It looks to me that it might be better to rewrite the model to a ForeignKey, this makes querying in Django more convenient:
class Major(models.Model):
major_cd = models.IntegerField(primary_key=True)
description = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = False
db_table = 'major'
class Percentages(models.Model):
major_cd = models.OneToOneField(
Major,
on_delete=models.CASCADE,
primary_key=True,
db_column='major_cd'
)
percentage = models.CharField(max_length=5, blank=True, null=True)
class Meta:
managed = False
db_table = 'percentages'
Once this is done, we can query with:
def home(request):
majors = Major.objects.filter(percentage__isnull=False)
context = {
'majors': majors
}
return render(request, 'website/index.html', context)
In the template, we can then render this with:
<select class="form-control select2">
<option>Select Major Head</option>
{% for major in majors %}
<option>{{ major.pk }}: {{ major.description }}</option>
{% endfor %}
</select>
you can use Q, from django.db.models import Q.
for example you can make it like ..
descrp = Major.objects.filter(Q(major_cd=.major_cd) & Q(m_description=m.description))

How to pass multiple values from models.py to HTML via views.py in Django

I have a following models.py for my Django blog, I made a following views.py to pass the value of the slug for my URL parameter.
However I am struggling to create a model in views to get other data(person & description) from Category class.
I have tried some patterns by myself but can not pass them to HTML. (always Error or not showing)
Can you please give me some idea of how to solve this.
models.py
class Category(models.Model):
person = models.CharField(max_length=20)
description = models.TextField()
slug = models.SlugField()
def __str__(self):
return self.person
views.py
def blog_category(request, category):
posts = Post.objects.filter(categories__slug__contains=category).order_by("-created_on").distinct()
context = {"category": category, "posts": posts}
return render(request, "blog_category.html", context)
HTML(Localhost:8000/slug)
{{ person }}
{{ description }}
this is full code of my models.py
class Category(models.Model):
person = models.CharField(max_length=20)
description = models.TextField()
slug = models.SlugField()
def __str__(self):
return self.person
class Recommender(models.Model):
recommender_name = models.CharField(max_length=20)
slug = models.SlugField()
def __str__(self):
return self.recommender_name
class Post(models.Model):
book_title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
book_link = models.CharField(max_length=255)
recommenders = models.ForeignKey("Recommender", on_delete=models.CASCADE,)
source = models.TextField()
source_link = models.CharField(max_length=255)
created_on = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
categories = models.ManyToManyField("Category", related_name="posts")
slug = models.SlugField()
def __str__(self):
return self.book_title
posts = Post.objects.filter(categories__slug__contains=category).order_by("-created_on").distinct()
Is going to return a queryset. It can have more than one instance of the model class (since you are using filter). In your context you are sending this queryset as posts to your templates.
So in your HTML you can use something like this. You need to use a for loop since there can be more than one item in posts.
{% for post in posts %}
{% for category in post.categories.all %}
{{ category.person }}
{{ category.description }}
{% endfor %}
{% endfor %}
I would look at this example.
Namely, if you render the template like it is shown in the example, you should be able to do
{{ category.person }} {{ category.description }}

Unable to automatically pick foreign key from modelform

I am working on a product app on Python 2.7 / Django 1.7.
I have a model for product namely 'product_profile' and I want to allow my customer (end user) to ask any thing regarding specific products using a form.
However I am unable to allow user to automatically select the product (foreign key) and the customer has to select from a drop-down which quite irrational. I have also assigned the foreign key in url-variable.
here is my code:
MODEL.PY
class ProductProfile(models.Model):
category = models.ForeignKey(Category)
brand = models.ForeignKey(Brand)
product_name = models.CharField(max_length=128)
model_name = models.CharField(max_length=128)
generation = models.CharField(max_length=128)
processor = models.CharField(max_length=128)
ram = models.DecimalField(max_digits=2, decimal_places=0)
hdd = models.DecimalField(max_digits=6, decimal_places=2)
optical_drive = models.CharField(max_length=128)
display = models.CharField(max_length=128)
card_reader = models.CharField(max_length=128)
blue_tooth = models.CharField(max_length=128)
web_cam = models.CharField(max_length=128)
warranty = models.CharField(max_length=128)
price = models.DecimalField(max_digits=9, decimal_places=2)
condition = models.TextField()
product_image = models.ImageField(upload_to=update_Product_image_filename)
post_date = models.DateTimeField(db_index=True, auto_now_add=True)
# Override th __unicode__() method to return out something meaningful!
def __unicode__(self):
return self.product_name
class Customer_ps_contact(models.Model):
name = models.CharField(max_length=128)
email = models.EmailField(max_length=75)
subject = models.CharField(max_length=128 )
product = models.ForeignKey(ProductProfile)
message = models.TextField()
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format:
'+999999999'. Up to 15 digits allowed.")
phone_number = models.CharField(validators=[phone_regex], blank=True, max_length=15) # validators should be a
list
def __unicode__(self):
return self.name
FORM.PY
class Customer_ps_contactForm(forms.ModelForm):
class Meta:
model = Customer_ps_contact
product = forms.ModelChoiceField(queryset=ProductProfile.objects.all(),
widget=forms.HiddenInput())
fields = ('name','email', 'product','subject','message', 'phone_number')
VIEWS.PY
def product_inquiry(request, product_id):
product = ProductProfile.objects.get(pk=product_id)
if request.method == 'POST':
#form = Customer_ps_contactForm(request.POST, initial = {'product': product})
#form = Customer_ps_contactForm(initial = {'product': product.id})
form = Customer_ps_contactForm(request.POST)
if form.is_valid():
form_data_dict = form.cleaned_data
print form_data_dict['product']
mail_customer_enquriy(form_data_dict) # Function to send email to admin
thank_u_customer(form_data_dict) # Function to send email to customers
form = form.save(commit=False)
form.product = product
form.save()
return home(request)
else:
print ("form is not valid")
print (form.errors)
else:
form = Customer_ps_contactForm()
context_dict = {'form':form, 'product': product}
return render(request, 'product/product_inquiry2.html',context_dict)
URL Patterns
urlpatterns = patterns('',
url(r'^inquiry/(?P<product_id>\d+)/$', views.product_inquiry, name='price'), # Only relevent url given
)
Template : product_inquiry2.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block body_block %}
{% block title %}Product Inquiry{% endblock %}
<div class="row">
<div class="col-md-10 col-md-offset-1">
<h2 style="font-weight:bold">Enquiry regarding '{{product.product_name}}'</h2>
<hr>
<form id="contact_form" method="post" action=""/>
{% csrf_token %}
{{ form | crispy }}
<input class="btn btn-primary pull-right " type="submit" name="submit" value="Submit the Message" />
</form>
</div>
</div>
{% endblock %}
What should I do?
You know what the product is from the id in the url, so there's no need to include it in your form.
To check that the product exists in the database, you can use the get_object_or_404 shortcut.
def product_inquiry(request, product_id):
product = get_object_or_404(ProductProfile, pk=product_id)
Then leave out 'product' from your list of fields, and remove the ModelChoiceField with hidden input widget.
class Customer_ps_contactForm(forms.ModelForm):
class Meta:
model = Customer_ps_contact
fields = ('name','email','subject','message','phone_number')
You are already setting the product when you save it, but it would be clearer to use the variable name instance to make it clearer what's going on. If you change your mail_customer_enquriy and thank_u_customer methods to use the instance instead of cleaned_data, then you won't have to do anything with form.cleaned_data.
if form.is_valid():
instance = form.save(commit=False)
instance.product = product
instance.save()
mail_customer_enquriy(instance) # Function to send email to admin
thank_u_customer(instance) # Function to send email to customers
return home(request)

Categories

Resources