Check if user had filled questionnaire before with Django - python

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 %}

Related

Django Filters on multiple models

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.

__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.

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 display relationship with back reference at django template through User?

I have such models
class Employee(models.Model):
"""Employee information."""
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='employee', unique=True)
position = models.CharField("current position in a company", max_length=64, blank=True)
birth_date = models.DateField("date of birth", null=True)
skills = models.ManyToManyField(
Technology, through="Skill", verbose_name="skills", blank=True)
class Technology(models.Model):
"""Technologies."""
name = models.CharField('technology name', max_length=32, unique=True)
class Skill(models.Model):
"""Information about an employee's skills."""
LEVELS = (
('basic', 'Basic'),
('intermediate', 'Intermediate'),
('advanced', 'Advanced'),
('expert', 'Expert'),
)
employee = models.ForeignKey(
Employee, on_delete=models.CASCADE, related_name="employee_skills")
technology = models.ForeignKey(Technology, on_delete=models.CASCADE)
start_date = models.DateField(
verbose_name='Works with since:')
level = models.CharField("level", max_length=64, choices=LEVELS)
And I can't understand why my template code doesn't work
template.html
{{ user.employee.position }}
{{ user.employee.birth_date }}
{{ user.employee.summary }}
{% for i in user.employee.skills.all %}
{{ i.technology.name }}
{{ i.level }}
{% endfor %}
I can't see absolutely nothing. All models possible to see at adminpanel. And else then I using TemplateView such as
class AccountView(TemplateView):
template_name = "profile.html"
def get_context_data(self, **kwargs):
context = super(AccountView, self).get_context_data(**kwargs)
context['skills'] =
Skill.objects.filter(employee__user=self.request.user)
return context
then everything works fine.
There is something wrong with the modeling. You should use a OneToOneField between Employee and User. In essence an OneToOneField is a unique ForeignKey. It will however change some logic, such that user.employee will access the related Employee object, not a QuerySet of Employees:
class Employee(models.Model):
# ...
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='employee'
)
# ...
In your AccountView, you unified 'skills' with the skills of that employee, indeed:
class AccountView(TemplateView):
template_name = "profile.html"
def get_context_data(self, **kwargs):
context = super(AccountView, self).get_context_data(**kwargs)
context.update(
skills=Skill.objects.filter(employee__user=self.request.user).select_related('technology')
)
return context
You might want to use .select_related(..) here to prevent the so-called "N+1 problem" where for each skill, you will make an extra query.
So you can render the skills with:
{% for skill in skills %}
{{ skill.technology.name }}
{{ skill.level }}
{% endfor %}
or you can access is through:
{% for skill in request.user.employee.employee_skills.all %}
{{ skill.technology.name }}
{{ skill.level }}
{% endfor %}
although the above is less safe, since it is possible that a User has no related Employee object.
Your Employee model currently has a one to many relationship with a user
class Employee(models.Model):
"""Employee information."""
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='employee', unique=True)
according to your comment you need a one to one relation ship so you need to change this to use a OneToOneField instead of a ForeignKey
Conceptually, this is similar to a ForeignKey with unique=True, but the “reverse” side of the relation will directly return a single object.

Django dealing with a model fields

I'm new to Django and I'm trying to learn as I go. And I've ended up in a situation where I can't figure out what is the best way forward.
snippet from models.py:
class ProjectMeta(models.Model):
project = models.ForeignKey(Project)
architect = models.CharField(max_length=200)
landscape = models.CharField(max_length=100, blank=True)
engineer = models.CharField(max_length=200, blank=True)
client = models.CharField(max_length=100)
consultant = models.CharField(max_length=100, blank=True)
size = models.DecimalField(max_digits=5, decimal_places=2, blank=True)
location = models.CharField(max_length=200)
date = models.DateField()
STATUS = (
('CP', 'Competition'),
('UC', 'Under construction'),
('CO', 'Completed'),
)
status = models.CharField(max_length=2, choices=STATUS, default=1)
And this is the view:
class ProjectDetailView(DetailView):
model = Project
def get_context_data(self, **kwargs):
context = super(ProjectDetailView, self).get_context_data(**kwargs)
context['projectmeta_list'] = ProjectMeta.objects.all()
return context
But if I want to output ProjectMeta in the template I could iterate over projectmeta_list.
{% for metadata in projectmeta_list %}
<p>Architect: {{ metadata.architect }}</p>
{% endfor %}
But this require alot of repeating myself, and I wont work. Because lets say the architect field is empty, I would get Archiect: printed to the page. Is there a built-in way of converting a model into a dict or list, so I can iterate over it and only print out fields that aren't empty to the page?
I've been looking at get_fields(), would that work? https://docs.djangoproject.com/en/1.10/ref/models/meta/#retrieving-all-field-instances-of-a-model
I tried this in the shell, threw me and AttributeError:
>>> from projects.models import *
>>> Project._projectmeta.get_fields()
You should try wrapping the <p>Architect: {{ metadata.architect }}</p> piece in a conditional {% if metadata.architect != '' %} or some condition to that effect.
Try with another ProjectMeta model. Take a look at this one.
class ProjectMeta(models.Model):
project = models.ForeignKey(Project)
name = models.CharField(max_length=50)
value = models.TextField()
And this query should work. myproject.projectmeta_set.filter(name="status")
You can use built-in default or default_if_none template filters to show a default value if it is None or empty string.
{% for metadata in projectmeta_list %}
<p>Architect: {{ metadata.architect|default:"-" }}</p>
{% endfor %}
Check this for more details.

Categories

Resources