how to get related objects from a many to many field - python

I am trying to get the objects from a many to many field. when the user selects their answer I want to be able to get the associated objects from the MtoM field.Then increment the related objects ansData + 1. ans in models was something else but i changed it for reasons, but that is what I am tryning to increment.
models.py
class User(models.Model):
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
#password = models.CharField(max_length=25)
email = models.EmailField(max_length=100)
class Quiz(models.Model):
name = models.CharField(max_length=200,primary_key=True)
NOQ = models.IntegerField(default=1)
class Meta:
verbose_name = "Quiz"
verbose_name_plural = "Quizzes"
def __str__(self):
return self.name
#number Of Questions
class Major(models.Model):
major = models.CharField(max_length=200)
ans = models.IntegerField(default=0)
answer = models.ManyToManyField('Answer')
def __str__(self):
return self.major
class Question(models.Model):
question_text = models.CharField(max_length=400)
quiz = models.ForeignKey("Quiz", on_delete=models.CASCADE, null=True)
def __str__(self):
return self.question_text
class Answer(models.Model):
question = models.ForeignKey('Question', on_delete=models.CASCADE, null=True)
answer_text = models.CharField(max_length=200)
def __str__(self):
return self.answer_text
class QuizTaker(models.Model):
user = models.ForeignKey("User", on_delete=models.CASCADE)
quiz = models.ForeignKey("Quiz", on_delete=models.CASCADE)
completed = models.BooleanField(default=False)
def __str__(self):
return self.user
views.py
class QuizView(DetailView):
model = Question
template_name = 'Quizzes/quiz.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'JSUMA/results.html'
def vote(request, question_id):
question = get_object_or_404(Question, question_id)
try:
selected_answer = question.answer_set.get(pk=request.POST['answer'])
except (KeyError,Answer.DoesNotExist):
return render(request, 'Quizzes/quiz.html,' {'question' : question,
'error_message' : "You didn't select an answer.",})
else:

This is very similar to the Official Django Tutorial showing the Polls App. I recommend you to once view it carefully.
Rest, you should add a number of votes integer field to every answer.
selected_answer = Answer.objects.get(pk=request.POST['answer'])
Now simply increase vote of this answer.

Related

Django generic create view . Can we add conditions to save data from the form?

This is what my code looks like and I want to add some condition to the Borrower create view like if the stock method of book returns 0 then don't list that book in field while creating a new borrower or if it isn't possible at least throw some error while adding borrower to that book.
models.py:
class Book(models.Model):
id = models.UUIDField(primary_key=True, unique=True,
default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
summary = models.TextField(
max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
help_text='13 Character https://www.isbn-international.org/content/what-isbn')
genre = models.ManyToManyField(
Genre, help_text="Select a genre for this book")
language = models.ForeignKey(
'Language', on_delete=models.SET_NULL, null=True)
total_copies = models.IntegerField()
pic = models.ImageField(blank=True, null=True, upload_to='books')
def stock(self):
total_copies = self.total_copies
available_copies = total_copies - \
Borrower.objects.filter(book=self).count()
if available_copies > 0:
return available_copies
else:
return 0
def __str__(self):
return self.title
class Borrower(models.Model):
id = models.UUIDField(primary_key=True, unique=True,
default=uuid.uuid4, editable=False)
student = models.ForeignKey('Account', on_delete=models.CASCADE)
book = models.ForeignKey('Book', on_delete=models.CASCADE)
issue_date = models.DateField(
null=True, blank=True, help_text='YYYY-MM-DD', default=date.today)
return_date = models.DateField(
null=True, blank=True, help_text='YYYY-MM-DD')
def __str__(self):
return self.student.name.title()+" borrowed "+self.book.title.title()
def fine(self):
today = date.today()
fine = 0
if self.return_date <= today:
fine += 5 * (today - self.return_date).days
return fine
views.py:
class BorrowerView(LoginRequiredMixin, ListView):
model=Borrower
context_object_name='borrowers'
template_name = 'library/borrower_list.html'
def get_context_data(self, **kwargs):
context=super().get_context_data(**kwargs)
if self.request.user.is_admin or self.request.user.is_superuser:
context['borrowers']=context['borrowers']
else:
context['borrowers']=context['borrowers'].filter(student = self.request.user.id)
return context
class BorrowerCreate(LoginRequiredMixin, UserAccessMixin, CreateView):
model=Borrower
permission_required= 'borrowers.add_borrowers'
fields='__all__'
success_url=reverse_lazy('library:borrower-list')
def form_valid(self, form):
form.instance.user=self.request.user
return super(BorrowerCreate, self).form_valid(form)
class BorrowerDetail(LoginRequiredMixin, DetailView):
model=Borrower()
context_object_name='borrower'
template_name='library/borrower.html'
class Book(models.Model):
id = models.UUIDField(primary_key=True, unique=True,
default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
summary = models.TextField(
max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
help_text='13 Character https://www.isbn-international.org/content/what-isbn')
genre = models.ManyToManyField(
Genre, help_text="Select a genre for this book")
language = models.ForeignKey(
'Language', on_delete=models.SET_NULL, null=True)
total_copies = models.IntegerField()
pic = models.ImageField(blank=True, null=True, upload_to='books')
#new, use this to keep track of available books
available_copies = models.IntegerField()
def __str__(self):
return self.title
When any borrower borrows a copy of the book, you will subtract it from the total copies.
class BorrowerCreate(LoginRequiredMixin, UserAccessMixin, CreateView):
model=Borrower
permission_required= 'borrowers.add_borrowers'
fields='__all__'
success_url=reverse_lazy('library:borrower-list')
#remember to get the object using slug or 404
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
book = Book.objects.get(id=instance.book.id)
#get the book id from the form and check if the book is still available, then subtract.
if book.available_copies > 0:
book.available_copies -= 1
book.save()
instance.save()
message.success(self.request, _("successful")
message.error(self.request, _("Book not in stock")
return super(BorrowerCreate, self).form_valid(form)
If user return the book and click returned. you can perform a similar action by adding to available copies.
This is not the solution, you can write a fat model with methods that takes care of both borrowing and return. Like this
def borrow(self):
self.available_copies -= 1
def returned(self):
self.available_copies += 1
You can call these two methods in different views or define a signal that call them using pre_save
Ist:
Instead of defining a new method which you called stock, why not add stock as a field instead of making a database query.
2nd:
calling the class inside the same class is not the best way to make queries inside the class.
3rd:
To add a condition while adding an object, you need to override the save method of the object like this.
def save(self, *args, **kwargs):
# do_something() here.....
# then
return super().save(*args, **kwargs)
The above code will enable you to perform any action before saving the object.
Another way you can do this is inside the form_valid function like this.
def form_valid(self, form):
instance = form.save(commit=False)
# commit = False will make sure that the form is not saved
# then you can now query the database and check conditions like
if Borrower.object.all().count() > 0:
instance.save()
messages.success(self.request, _("saved successfully")
else:
messages.error(self.request, _("Error")
return redirect("URL")

Saving value of one model field count to another field of the same model during save

I am working on a pool app and want to add the total_likes attribute to the model which will allow me to count the number of users who liked the question, just like Youtube community question allows.
I just tried to override the save(*args, **kwargs) but got many errors.
What should I do now?
from django.db import models
from django.contrib.auth.models import User
class Question(models.Model):
text = models.TextField()
voters = models.ManyToManyField(to=User, blank=True, related_name='voters')
impression = models.CharField(max_length=30, choices=impression_choices, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True)
likes = models.ManyToManyField(to=User, related_name='likes', blank=True)
total_likes = models.IntegerField(default=0)
def __str__(self):
return self.text
def save(self, *args, **kwargs):
self.total_likes = self.likes_set.count()
super().save(*args, **kwargs)
The Model below works totally fine. In the model above I tried to override the save() method but I don't know how?
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.CharField(max_length=20)
votes = models.IntegerField(blank=True, default=0)
def __str__(self):
return self.text

Getting the entries of a ManyToManyField

I'm working with a ManyToManyField and using a ModelMultipleChoice on form, I want to get the entries, but all I get is appname.Extra.none
models.py
class Extra(models.Model):
extra_n = models.CharField(max_length=200)
extra_price = models.IntegerField(default=0)
def __str__(self):
return self.extra_n
class Meal(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.PROTECT)
category = models.ForeignKey(MealCategory, on_delete=models.PROTECT)
name = models.CharField(max_length=500)
short_description = models.CharField(max_length=500)
image = models.ImageField(upload_to='meal_images/', blank=False)
price = models.IntegerField(default=0)
extras = models.ManyToManyField(Extra, related_name='extras')
def __str__(self):
return self.name
forms.py
class MealForm(forms.ModelForm):
extras = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=Meal.extras)
class Meta:
model = Meal
exclude = ("restaurant",)
views.py
def restaurant_meal(request):
meals = Meal.objects.filter(restaurant = request.user.restaurant).order_by("-id")
return render(request, 'restaurant/meal.html', {"meals": meals})
The output desired is getting the extras added displayed on restaurant_meal view.
you can try change
meals = Meal.objects.filter(restaurant = request.user.restaurant).order_by("-id")
to
meals = Meal.objects.filter(restaurant = request.user.restaurant).prefetch_related('extras').order_by("-id")
and try again.
Doc of this in prefetch_related

Django ModelForm ForeignKey query

I want to have a form which only offers the user to post a question for a project he is participating in.
models.py:
class Project(models.Model):
project_name = models.CharField(max_length=255, unique=True, blank=False)
def __str__(self):
return str(self.project_name)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
project = models.ManyToManyField(Project)
def __str__(self):
return str(self.user)
class Question(models.Model):
title = models.CharField(max_length=255, blank=False)
content = tinymce_models.HTMLField(blank=False)
author = models.ForeignKey(User, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
...
def __str__(self):
return str(self.title)
class QuestionForm(ModelForm):
class Meta:
model = Question
fields = ['title', 'content', 'project']
in views.py:
form = QuestionForm()
form.fields["project"].queryset = Project.objects.filter(project_name__in=request.user.profile.project.all())
But somehow the result of the query always stays empty.
Does somebody maybe have an idea what I am missing?
Your query is over complicated. You should just use the user's projects directly:
form.fields["project"].queryset = request.user.profile.project.all())

django quiz app model for multiple choice questions

I am creating quiz app in django, my django model for questions is like this,
class Question(models.Model):
questions = models.CharField(max_length=50, unique=True)
choice1 = models.CharField(max_length=50, unique=True)
choice2 = models.CharField(max_length=50, unique=True)
choice3 = models.CharField(max_length=50, unique=True)
choice4 = models.CharField(max_length=50, unique=True)
correct_answer = models.CharField(max_length=50, unique=True)
is this fine or save the four options in postgres array or save the choices in separate table.
For a properly normalized relational database schema, you want a distinct Choice model with a foreign key on Question:
class Question(models.Model):
question = models.CharField(...)
class Choice(models.Model):
question = models.ForeignKey("Question", related_name="choices")
choice = modelsCharField("Choice", max_length=50)
position = models.IntegerField("position")
class Meta:
unique_together = [
# no duplicated choice per question
("question", "choice"),
# no duplicated position per question
("question", "position")
]
ordering = ("position",)
And then you can get at a Question's choices with myquestion.choices.all() (and get the question from a Choice with mychoice.question).
Note that this won't impose any limitation on the number of choices for a Question, not even mandates that a Question has at least one related Choice.
Unless you have a very compelling reason to do otherwise, a properly normalized schema is what you want when using a relational database (rdbms are much more than mere bitbuckets, they offer a lot of useful features - as long as you do have a proper schema, that is).
This tutorial shows it all https://medium.com/#nsjcorps/create-a-quiz-application-with-django-rest-framework-react-redux-part-one-f0fcae5103fd
This is the summary of it though:
from django.db import models
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
class Quiz(models.Model):
name = models.CharField(max_length=1000)
questions_count = models.IntegerField(default=0)
description = models.CharField(max_length=70)
created = models.DateTimeField(auto_now_add=True,null=True,blank=True)
slug = models.SlugField()
roll_out = models.BooleanField(default=False)
class Meta:
ordering = [‘created’,]
verbose_name_plural =”Quizzes”
def __str__(self):
return self.name
class Question(models.Model):
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
label = models.CharField(max_length=1000)
order = models.IntegerField(default=0)
def __str__(self):
return self.label
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.CharField(max_length=1000)
is_correct = models.BooleanField(default=False)
def __str__(self):
return self.text
class QuizTakers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
correct_answers = models.IntegerField(default=0)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.username
class Response(models.Model):
quiztaker = models.ForeignKey(QuizTakers, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
answer = models.ForeignKey(Answer,on_delete=models.CASCADE,null=True,blank=True)
def __str__(self):
return self.question.label
#receiver(post_save, sender=Quiz)
def set_default_quiz(sender, instance, created,**kwargs):
quiz = Quiz.objects.filter(id = instance.id)
quiz.update(questions_count=instance.question_set.filter(quiz=instance.pk).count())
#receiver(post_save, sender=Question)
def set_default(sender, instance, created,**kwargs):
quiz = Quiz.objects.filter(id = instance.quiz.id)
quiz.update(questions_count=instance.quiz.question_set.filter(quiz=instance.quiz.pk).count())
#receiver(pre_save, sender=Quiz)
def slugify_title(sender, instance, *args, **kwargs):
instance.slug = slugify(instance.name)
you can use arryfield if you are using postgres sql
class QuizMcqDetail(BaseModel):
title = models.CharField(max_length=255, null=True, blank=True)
time_limit = models.TimeField()
start_date = models.DateTimeField(null=True, blank=True)
end_date = models.DateTimeField(null=True, blank=True)
available_languages = models.ManyToManyField(QuizLanguage, related_name='available_language', blank=True)
question_count = models.PositiveIntegerField(default=10)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.title
class QuizMcqQuestion(BaseModel):
s_no = models.PositiveIntegerField()
quiz = models.ForeignKey(QuizMcqDetail, on_delete=models.CASCADE, related_name='quiz_mcq')
question_text = models.TextField(max_length=200)
options = ArrayField(models.CharField(max_length=200))
answer_position = ArrayField(models.IntegerField())
explanation = models.TextField(null=True)
language = models.ForeignKey(QuizLanguage, related_name='question_language', on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.question_text
class QuizMcqSelect(BaseModel):
quiz_question = models.ForeignKey(QuizMcqQuestion, related_name="select_quiz_mcq", on_delete=models.CASCADE)
user = models.ForeignKey(User, related_name="user_select_quiz_mcq", on_delete=models.CASCADE)
answer = models.BooleanField(default=False)
selected_answer = ArrayField(models.IntegerField())
def __str__(self):
return f"{self.quiz_question} {self.user} {self.answer}"
def save(self, *args, **kwargs):
if self.quiz_question.answer_position == self.selected_answer:
self.answer = True
else:
self.answer = False
super().save(*args, **kwargs)
The best way is:
ANSWER_CHOICES = (
("choice_1", "Answer_1"),
("choice_2", "Answer_2"),
("choice_3", "Answer_3"),
("choice_4", "Answer_4"),
("choice_5", "Answer_5"),
("choice_6", "Answer_6"),
)
class Question(models.Model):
questions = models.CharField
(
max_length=50,
unique=True,
choices=ANSWER_CHOICES
)
correct_answer = models.CharField(max_length=50, unique=True)

Categories