django prefetch_related() attribute error - python

How to properly user prefetch_related with ManyToMany relation
my model:
class Subject(models.Model):
subject_name = models.CharField(max_length=150)
subject_code = models.CharField(max_length=50)
year = models.ForeignKey(YearLevel, null=True, on_delete=models.SET_NULL)
units = models.CharField(max_length=10)
def __str__(self):
return self.subject_name + ' ' + '(' + self.subject_code + ')'
class Student(models.Model):
student = models.ForeignKey(settings.AUTH_USER_MODEL, limit_choices_to= Q(is_student=True), on_delete= models.CASCADE)
enrolled_subject = models.ManyToManyField(Subject)
my view:
def home(request):
verses = VerseOfTheDay.objects.all()
news = Announcement.objects.all()
student_grade_form = AddStudentGradeForm()
students = Student.objects.all().prefetch_related('subject_set')
context = {
"verse": verses,
'news': news,
'form': student_grade_form,
'students': students,
}
return render(request, 'sample.html', context)
my html:
{% for student in students %}
<p>
<a class="btn btn-primary" data-bs-toggle="collapse" href="#collapse{{forloop.counter}}" role="button" aria-expanded="false" aria-controls="collapse{{forloop.counter}}">
{{student.student}}
</a>
</p>
<div class="collapse" id="collapse{{forloop.counter}}">
<div class="card card-body">
{% for subject in student.subject_set.all %}
{{subject.subject}}
{% endfor %}
</div>
</div>
{% endfor %}
I am getting an error:
AttributeError at /
Cannot find 'subject_set' on Student object, 'subject_set' is an invalid parameter to prefetch_related()

Change this line
students = Student.objects.all().prefetch_related('subject_set')
to
students = Student.objects.all().prefetch_related('enrolled_subject')

Related

Django - get objects from one table who belong to another objects in other table

I have a project to do which consists to creating a note manager in Django. So I created my tables in sql with foreign keys. And I have been facing a problem for several days. I have a page that lists all the students in the database, and I would like by clicking on a link, to be able to display all the notes belonging to each student.
Here's my SQL tables (étudiants = students / Notes = grades) :
sql students table /
sql grade table
models.py :
class Etudiants(models.Model):
numeroetudiant = models.BigIntegerField(db_column='numeroEtudiant', blank=True, null=True) # Field name made lowercase.
nom = models.CharField(max_length=255, blank=True, null=True)
prenom = models.CharField(max_length=255, blank=True, null=True)
groupe = models.BigIntegerField(blank=True, null=True)
photo = models.TextField(blank=True, null=True)
email = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'etudiants'
def __str__(self):
return self.nom + " " + self.prenom
class Notes(models.Model):
examens = models.ForeignKey(Examens, models.DO_NOTHING, db_column='examens', blank=True, null=True)
etudiant = models.ForeignKey(Etudiants, models.DO_NOTHING, db_column='etudiant', blank=True, null=True)
note = models.BigIntegerField(blank=True, null=True)
appreciation = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'notes'
def __str__(self):
return "Note de " + self.etudiant.nom + " " + self.etudiant.prenom + " à l'examen de " + self.examens.titre
views.py :
def etudiants(request):
etudiants = Etudiants.objects.all()
return render(request, 'etudiants.html', {'etudiants': etudiants, 'notes': notes})
def affichenote(request, id):
notes = Notes.objects.all()
return render(request, 'affichenote.html', {'notes': notes})
urls.py :
path('etudiants/', views.etudiants, name='etudiants'),
path('affichenote/<int:id>/', views.affichenote, name='affiche-note'),
etudiants.html :
{% extends 'base.html' %} {% block content %}
<div class="row">
{% for Etudiants in etudiants %}
<div class="col-sm-4">
<div class="card">
<img src="..." class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">{{ Etudiants.nom }} {{ Etudiants.prenom }} </h5>
<p class="card-text">
E-mail : {{ Etudiants.email }} <br>
Numéro étudiant : {{ Etudiants.numeroetudiant }} <br>
Groupe : {{ Etudiants.groupe }} <br>
</p>
Voir les notes
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
If you want by clicking on a link, you would be able to display all the notes belonging to each student. So you can apply filter() through student id in the following way:
views.py
def etudiants(request):
etudiants = Etudiants.objects.all()
return render(request, 'etudiants.html', {'etudiants': etudiants})
def affichenote(request, id):
notes = Notes.objects.filter(etudiant__id=id)
return render(request, 'affichenote.html', {'notes': notes})
urls.py
urlpatterns = [
path('etudiants/', views.etudiants, name='etudiants'),
path('affichenote/<int:id>/', views.affichenote, name='affichenote'),
]
etudiants.html
<div class="row">
{% for etudiant in etudiants %}
<div class="col-sm-4">
<div class="card">
<img src="..." class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">{{ etudiant.nom }} {{ etudiant.prenom }} </h5>
<p class="card-text">
E-mail : {{ etudiant.email }} <br>
Numéro étudiant : {{ etudiant.numeroetudiant }} <br>
Groupe : {{ etudiant.groupe }} <br>
</p>
Voir les notes
</div>
</div>
</div>
{% endfor %}
</div>
Then, in affichenote.html, you can access all notes, relating to a student.
affichenote.html
<body>
{% for note in notes %}
<p>{{note.edudiant}}</p>
<p>{{note.note}}</p>
<p>{{note.appreciation}}</p>
{% endfor %}
</body>
Note: Models in django generally written in singular form, it will be better if you name it Etudiant and Note instead of Etudiants and Notes respectively, as s is already itself added as the suffix for every model.

Problem with Select a valid choice. That choice is not one of the available choices

in my form.py I have a class StudentsForm:
class StudentsForm(ModelForm):
def __init__(self, *args, **kwargs):
students = kwargs.pop('students')
course = kwargs.pop('course')
super().__init__(*args, **kwargs)
CHOICE_LIST = [('', '----')]
i = 1
for itm in students:
CHOICE_LIST.append((i, itm))
i += 1
self.fields['students'].choices = CHOICE_LIST
self.fields['students'].initial = ['']
self.fields['course'].choices = [(1, course), (2, '----'
)]
self.fields['course'].initial = [1]
class Meta:
model = StudCourse
fields = (
'course',
'students',
)
widgets = {
'course': forms.Select(attrs={'class': 'form-control',
'placeholder': 'Select course',
'style': 'color: crimson; background-color:ivory;'
}),
'students': forms.SelectMultiple(attrs={'class': 'form-control'
, 'placeholder': 'Select students',
'style': 'color: crimson; background-color:ivory;'
}),
}
my view.py
def test(request):
#team = get_team(request)
# here the second students is a query set of model Student, 2nd course is an object of model #Course
form = StudentsForm(students=students, course=course)
if request.method == 'POST':
form = StudentsForm(request.POST, students=students, course=course)
if form.is_valid():
form.save()
return redirect('home')
if team.exists():
return render(request, 'app/students_goal_form.html', {'form':form})
my students_goal_form.html
{% block content %}
<section id="students_form">
<div class="row">
<p>&nbsp</p>
</div>
<div class="row">
<div class="col-3"></div>
<div class="col-6">
<div class="form-group">
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-success">
<i class="icon-circle-arrow-right icon-large"></i> Save
</button>
</form>
</div>
</div>
<div class="col-3"></div>
</div>
<div class="row">
<p>&nbsp</p>
</div>
<div class="row">
<p>&nbsp</p>
</div>
<div class="row">
<p>&nbsp</p>
</div>
</section>
{% endblock %}
It works correctly without adding students=students, course=course to form (without modifying __init__ ) , but I need to filter query set in case of students and also need a specific (only one) course. I have also modified __init__ in form to add possibility to choose nothing.
my model StudCourse:
class StudCourse(models.Model):
course = models.ForeignKey(Course, verbose_name='Course', on_delete = models.CASCADE, related_name='+', blank=True, null=True, help_text=u'You can choose specific course or group of students')
students = models.ManyToManyField(Student, null=True, blank=True, symmetrical=False, related_name='student_name')
def get_students(self):
return "\n ".join([p.end_user_id for p in self.students.all()])
def __str__(self):
return 'Title: : ' + self.course
Why I have an Error when in view the form is validating: Select a valid choice. That choice is not one of the available choices.
How to modify my form which accept to choose nothing?
[UPDATE] my Student model:
class Student(models.Model):
name = models.CharField(max_length=30, verbose_name='Name')
lastname = models.CharField(max_length=30, verbose_name='Lastname')
status = models.BooleanField(default=False, verbose_name='Is Active?')
def __str__(self):
return self.name + ' ' + self.lastname
in view I set to the form next:
students = Student.objects.filter(status=true)
The code above doesn't explain in detail how do you generate content for the students variable that you pass to the form, but I guess the issue could be there.
The list of choices for the student's field is generated in a way that in each pair (id, object) the id is a number in a sequence but not the real id of an object.
CHOICE_LIST = [('', '----')]
i = 1
for itm in students:
CHOICE_LIST.append((i, itm))
i += 1
self.fields['students'].choices = CHOICE_LIST
instead, you should use the primary key as an id to reflect the structure of your database
CHOICE_LIST.append((itm.pk, itm))

Related Field got invalid lookup: icontains Django

So I'm trying to make an search option where users can search via categories and name but only name worked, when i used icontains on category it just gave me an error but did not give me an error with name also categories just does not load in HTML. Categories are supposed to load in <select> tag but it is not working, searchall function handles the page I'm talking about and search.html is the page.
this is the complete error
views.py
def searchall(request):
getobject = request.GET['search']
getcategory = request.GET['category']
if getcategory == 'All':
getcategory = ""
search = Book.objects.filter(name__icontains=getobject, category__icontains=getcategory)
oof = CartItem.objects.filter(user=request.user).values_list('book', flat=True)
lmao = OrderItem.objects.filter(user=request.user).values_list('book', flat=True)
hehe = CartItem.objects.filter(user=request.user)
category = Category.objects.all()
fianlprice = 0
for item in hehe:
fianlprice += item.book.price
books = Book.objects.all()
return render(request, 'main/search.html', {'books':search, 'price':fianlprice, 'cart':oof, 'order':lmao, 'category':category})
search.html
<h1>search</h1>
<h1>{{ error }}</h1>
<h1>Your cart currently costs ${{ price }}</h1>
<form method="GET" action="">
<input type="text" placeholder="Search here" name="search" id="search">
<button type="submit">Search</button>
</form>
{% for book in books %}
<h3>{{ book.name }}</h3>
<img src= "/media/{{ book.image }}" alt="">
<p>{{ book.description }}</p>
{% if book.id in cart %}
<form method="POST" action="/removefromcartforproducts/">
{% csrf_token %}
<button type="submit" name="removeid" value="{{ book.id }}">remove item from cart</button>
</form>
{% elif book.id in order %}
<h3>You already own this</h3>
{% else %}
<form method="POST" action="/addtocartforproducts/">
{% csrf_token %}
<button type="submit" name="bookid" value="{{ book.id }}">Add to cart</button>
</form>
{% endif %}
{% endfor %}
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
class Book(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
image = models.ImageField()
price = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.name
class OrderItem(models.Model):
order_id = models.CharField(max_length=10)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.user.username
class CartItem(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.user.username
In your searchall method, you are trying to filter category. But, as your Book model have category property which is object not a string. So, you have to go further inside it and compare the name of that category.
So:
def searchall(request):
getobject = request.GET['search']
getcategory = request.GET['category']
if getcategory == 'All':
getcategory = ""
search = Book.objects.filter(name__icontains=getobject, category__name__icontains=getcategory)
oof = CartItem.objects.filter(user=request.user).values_list('book', flat=True)
lmao = OrderItem.objects.filter(user=request.user).values_list('book', flat=True)
hehe = CartItem.objects.filter(user=request.user)
category = Category.objects.all()
fianlprice = 0
for item in hehe:
fianlprice += item.book.price
books = Book.objects.all()
return render(request, 'main/search.html', {'books':search, 'price':fianlprice, 'cart':oof, 'order':lmao, 'category':category})
Refs on span relationship lookups(__).

IntegrityError at /add_to_cart/ NOT NULL constraint failed:

I have a view which adds the items to the cart, but when I try to add the items to the cart Item model rising an error NOT NULL constraint failed: cart_item.product_id
I have created a model Item to capture the selected items
Here is my models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Product(models.Model):
product_title = models.CharField(null=True, blank=True,max_length=200)
product_price = models.IntegerField(null=True, blank=True,)
product_image = models.ImageField(upload_to='cart/products/', null=True, blank=True)
def __str__(self):
return self.product_title
class Cart_Bucket(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
create_date = models.DateTimeField(null=True, blank=True,verbose_name='create_date', auto_now=True)
checked_out = models.BooleanField(default=False, verbose_name='checked_out')
def __unicode__(self):
return unicode(self.create_date)
class Item(models.Model):
cart = models.ForeignKey(Cart_Bucket, verbose_name='cart', on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(verbose_name='quantity')
product = models.ForeignKey(Product, verbose_name='product', related_name='product', on_delete=models.CASCADE)
def __str__(self):
return u'%d units' % (self.quantity)
Views.py
def add_to_cart(request):
user = request.user
if not user.is_authenticated:
chars = string.ascii_uppercase + string.digits
user_name = ''.join(random.choice(chars) for _ in range(9))
password = '1234567a'
user = User.objects.create(username=user_name, first_name='guest', last_name='guest', email='guest#gmail.com', is_active=True, is_staff=True)
user.set_password(password)
user.save()
user = authenticate(username=user_name, password=password)
if user:
login(request, user)
product_id = request.GET.get('product_id')
cart = Cart_Bucket.objects.filter(checked_out=False, user=user)
cart = cart[0] if cart else ''
if not cart:
cart = Cart_Bucket.objects.create(user=user)
Item.objects.create(cart=cart, product_id=product_id, quantity=1)
print(Item.objects.all)
return render(request, 'products/products.html')
my products.html
{% extends 'home/base.html' %}
{% block body %}
<div class="row">
{% for products in product %}
<div class="col-md-6 col-centered">
<img src="{{ products.product_image.url }}" style="width: 30%;display:block;margin:0 auto;">
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6">
<br>
<p>{{ products.product_title }} Price : $ {{ products.product_price }} </p>
</div>
<div class="col-md-6"></div>
<div class="col-md-4">
Add to Cart
</div>
<div class="col-md-2"></div>
</div>
<hr>
</div>
{% endfor %}
</div>
{% endblock %}
I want to see the add_to_cart items on the cart page
I think that raises the problem because Item.objects.create() tries to save the model to the database first, before parsing all arguments. Based on Django documentation, .save() method has some advantages over create() method. This problem should be resolved if you change your code like:
newItem = Item()
newItem.cart = cart
newItem.product_id = product_id
newItem.quantity = 1
newItem.save()

Displaying all Django-taggit tags for each given object

I'm trying to display all the django-taggit tags related to the given object when I'm querying.
I've tried adding this function within my search_result.html template like this but no tags are being displayed:
{% if page_obj.object_list %}
<ol class="row top20">
{% for result in page_obj.object_list %}
<div class="showcase col-sm-6 col-md-4">
<a href="{{ result.object.get_absolute_url }}">
<h3>{{result.object.title}}</h3>
<img src="{{ result.object.image }}" class="img-responsive">
</a>
<!-- I want to display them in the span below -->
<div class="text-center">
<span class="label label-info">{{ result.object.ptags.name }}</span>
</div>
</div>
{% endfor %}
</ol>
</div>
{% endif %}
My Models:
class Product(models.Model):
title = models.CharField(max_length=255, default='')
slug = models.SlugField(null=True, blank=True, unique=True, max_length=255, default='')
description = models.TextField(default='')
ptags = TaggableManager()
timestamp = models.DateTimeField(auto_now=True)
def _ptags(self):
return [t.name for t in self.ptags.all()]
def get_absolute_url(self):
return reverse('product',
kwargs={'slug': self.slug})
def __str__(self):
return self.title
My custom forms.py function:
from haystack.forms import FacetedSearchForm
class FacetedProductSearchForm(FacetedSearchForm):
def __init__(self, *args, **kwargs):
data = dict(kwargs.get("data", []))
self.ptag = data.get('ptags', [])
super(FacetedProductSearchForm, self).__init__(*args, **kwargs)
def search(self):
sqs = super(FacetedProductSearchForm, self).search()
if self.ptag:
query = None
for ptags in self.ptag:
if query:
query += u' OR '
else:
query = u''
query += u'"%s"' % sqs.query.clean(ptags)
sqs = sqs.narrow(u'ptags_exact:%s' % query)
return sqs
And I'm passing the forms into the views like this:
class FacetedSearchView(BaseFacetedSearchView):
form_class = FacetedProductSearchForm
facet_fields = ['ptags']
template_name = 'search_result.html'
paginate_by = 6
context_object_name = 'object_list'
How can I do this?
Can you try this instead
<span class="label label-info">{{ result.object.ptags.names }}</span>
You can loop over the ptags.names queryset to display individual tags, like this:
{% for tag in result.object.ptags.names %}
{{ tag }}
{% endfor %}

Categories

Resources