Inside Django class-base CreateView, how to init intermediate model - django-views

I have three models, PiItem is the interface of Pi and Items models by "through".
Right now I want to create a new Items instance with new Pi instance and new PiItem instance inside Django CreatedView class.
Somehow I keep getting error message
*
*****MultipleObjectsReturned at /product/ get() returned more than one PiItem*****
*
Any help will be highly appreciated
THANKS
here are the models
class Pi(models.Model):
company = models.ForeignKey(CompanyProfile, related_name='pi', null=True)
contact = models.ForeignKey(Contact, null=True)
def __unicode__(self):
#return u'%s, %s' %(self.company.companyprofile_name, self.reference_id)
return u'%s' %(self.company.companyprofile_name)
class Items(models.Model):
product_id = models.CharField("Product ID",max_length=50, blank=False, null=True, unique=True)
in_pi = models.ManyToManyField(Pi, through='PiItem', blank=True)
def __unicode__(self):
return self.product_id
class PiItem(models.Model):
item_name= models.ForeignKey(Items)
pi = models.ForeignKey(Pi)
def __unicode__(self):
return self.pi.reference_id
and here is my view.py
from django.views.generic.edit import CreateView
from pi.models import Items, PiItem, Pi
from django.shortcuts import get_object_or_404
class AddItemView(CreateView):
context_object_name = 'Product_list'
model = Items
template_name = "product.html"
fields = "__all__"
def get_initial(self):
in_pi = get_object_or_404(PiItem)
return {
'in_pi':in_pi
}
and here is the template
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block pi %}
<form action="" method="post">{% csrf_token %}
{{ form|crispy }}
{{ form.get_form_class() }}
<input type="submit" value="Create" />
</form>

Problem most likely is here:
in_pi = get_object_or_404(PiItem)
You need to filter the PiItem object by passing additional arguments, such as foreigk key to each related model, like:
in_pi = get_object_or_404(PiItem, item_name=something1, pi=something2)

Related

Django Error: user_register_model matching query does not exist

How to fix This Error I'm Trying To Fix This Error But I Get Again And Again
i want to detect user who fill the form for example test fill the form but when i write the code down below i get this error
Any Help Will Be Appreciated!
ERROR
user_register_model matching query does not exist.
ERROR SCREENSHOT
Here is my Views.py
def buy_form(request):
if request.method == 'POST':
usr_buy = user_buy_form(request.POST)
if usr_buy.is_valid():
usr_buys = usr_buy.save(commit=False)
user_register_obj = user_register_model.objects.get(user=request.user)
usr_buys.users = user_register_obj
usr_buys.save()
else:
return print(usr_buy.errors)
else:
usr_buy = user_buy_form()
context = {'usr_buy':usr_buy}
return render(request,'user_buy.html',context)
Here is my Models.py
class user_register_model(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
join_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.user.username
class user_buy(models.Model):
users = models.ForeignKey(User,on_delete=models.CASCADE)
title = models.CharField(max_length=200)
payment_method = models.CharField(max_length=500)
price = models.IntegerField()
Trade_limits = models.IntegerField()
Location = models.CharField(max_length=1000)
def __str__(self):
return self.users.user.username
Here is my Forms.py
class user_buy_form(forms.ModelForm):
class Meta():
model = user_buy
fields = '__all__'
exclude = ('users',)
Here is my user_buy.html
{% extends 'base.html' %}
{% block body_block %}
<form class="form-control" method="POST">
{% csrf_token %}
{{usr_buy.as_p}}
<input type="submit" class="btn btn-primary" value="Submit">
</form>
{% endblock %}
I didn't see any points here to create the user_register_model.If you are trying to add the currently logged in user you can do this:
request.user will give you the currently logged in user so for this the user must be logged in.
#login_required
def buy_form(request):
if request.method == 'POST':
usr_buy = user_buy_form(request.POST)
if usr_buy.is_valid():
usr_buys = usr_buy.save(commit=False)
usr_buys.users = request.user
usr_buys.save()
return redirect('some_path') # redirect to some path after saving the form
Class names should normally use the CapWords convention.
I think the request.user is not present in the user_register_model model thats why it is giving matching query doesnot exist error, first create it in the user_register_model and then query it.

django form drop-down choices are displayed "objects" instead of string representation of the model __str__

In my django form I am using a method to filter the drop down options to the ones that are related to the logged-in user. After the implementation the displayed values changed to objects rather than the __str__ value. I am posting the simplified codes and a snapshot that shows this. I have followed everything needed, but I cannot figure out why this is happening:
models.py
class Business(models.Model):
client=models.ForeignKey('Client',on_delete=models.CASCADE, limit_choices_to={'is_active':True},)
name=models.CharField(max_length=30,blank=False, unique=True,)
def __str__(self):
return self.name
class MMRequestAttributes(models.Model):
client=models.ForeignKey('Client',on_delete=models.CASCADE, limit_choices_to={'is_active':True},)
business=models.ForeignKey('Business', on_delete=models.CASCADE,limit_choices_to={'is_active':True},)
class Ticket(MMRequestAttributes):
no=models.CharField('Ticket Number',max_length=50,default=uuid.uuid4,null=False, blank=False, editable=False, unique=True)
subject=models.CharField('Subject',max_length=100,null=False, blank=False)
description=models.TextField('Description',max_length=500,null=True,blank=True)
created_at=models.DateTimeField('Created at',auto_now_add=True, editable=False)
updated_at=models.DateTimeField('Updated at',auto_now=True, editable=False)
created_by= models.ForeignKey(settings.AUTH_USER_MODEL)
status=StateField(editable=False)
def __str__(self):
return 'Ticket #' + str(self.pk)
views.py
def new_ticket(request):
form=NewTicket(request.user)
return render(request,'mmrapp/new_ticket.html',{'form':form})
admin.py
class UserExtend(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, blank=False,null=False,)
client=models.ForeignKey('Client', on_delete=models.CASCADE,limit_choices_to={'is_active': True},)
forms.py
from django import forms
from .models import Ticket, Business
from .admin import UserExtend
from django.forms import ModelChoiceField
class NewTicket(forms.ModelForm):
def __init__(self,user, *args, **kwargs):
super(NewTicket, self).__init__(*args, **kwargs)
try:
client_id = UserExtend.objects.values_list('client_id', flat=True).get(user=user)
self.fields['business'].queryset=Business.objects.filter(client__id=client_id)
except UserExtend.DoesNotExist:
### there is not userextend corresponding to this user, do what you want
pass
class Meta:
model=Ticket
fields = ('subject','business')
new-ticket.html
{% extends 'mmrapp/__l_single_column.html' %}
{% load static %}
{% block main_col %}
<h1>New Ticket</h1>
<form method="POST" class="new-ticket">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Submit</button>
</form>
{% endblock main_col %}
Finally I found the problem. Somehow django cannot load the original models' string representation of their objects (__str__) when showing them as choices in the drop downs. I had to explicitly define them again inside the from model. The attribute is called label_from_instance. The good thing is this way one can display them different from what they are originally defined in the models.
so the monkey patch would be:
self.fields['business'].label_from_instance = self.business_label
#staticmethod
def business_label(self):
return str(self.name)
In Python 2.7 we were using
def unicode(self):
return self.name
After the python upgrade from 2.7 to 3.6 it was showing all object references in the dropdown so I added
def str(self):
return self.name
I added this method to the model in question:
def __str__(self):
return self.whatever_prop_suits_your_needs

What is wrong with the form?

I am trying to create a online Phone Book for my locality. I am getting problem with the app's entry creation view.
Here is my PhoneEntry model:
# coding=utf-8
from django.urls import reverse_lazy
from django.db import models
from django.template.defaultfilters import slugify
from phonenumber_field.modelfields import PhoneNumberField
class PhoneEntry(models.Model):
# Name of the organisation
org_name = models.CharField(max_length=100, verbose_name="Organisation's Name")
org_details = models.CharField(max_length=500, blank=True, verbose_name="Organisation's Details")
slug = models.SlugField(default='slug', unique=True)
# Verified or not
verified = models.BooleanField(default=False, blank=True)
# Dates when the entry was added and verified
added_date = models.DateField(auto_now_add=True, editable=False)
verified_date = models.DateField(auto_now_add=True)
last_edited_date = models.DateField(blank=True, null=True, auto_now_add=True)
# The phone numbers of the organisation
primary_ph_number = PhoneNumberField(verbose_name="Primary Phone Number")
secondary_ph_number = PhoneNumberField(verbose_name="Secondary Phone Number", blank=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(PhoneEntry, self).save(force_insert, force_update, using, update_fields)
self.slug = slugify(self.org_name+"-"+str(int(self.id)))
super(PhoneEntry, self).save(force_insert, force_update, using, update_fields)
#staticmethod
def get_absolute_url():
return reverse_lazy('phbook:index')
def __str__(self):
return self.org_name+"-"+str(self.primary_ph_number)
class Meta:
verbose_name_plural = "Phone Entries"
And this the EntryCreateForm:
class EntryAddForm(forms.ModelForm):
"""org_name = forms.CharField(max_length=100, label="Enter your organisation name: ")
org_details = forms.CharField(max_length=100, widget=forms.Textarea,
label="Enter your organisation details: ", required=False)"""
primary_ph_number = PhoneNumberField(label="Enter your primary phone number: ")
secondary_ph_number = PhoneNumberField(label="Enter your secondary phone number: ", required=False)
class Meta:
model = PhoneEntry
exclude = ['slug', 'last_edited_date', 'added_date', 'verified_date', 'verified']
And this the EntryAddView:
class EntryCreateView(CreateView):
model = PhoneEntry
form_class = EntryAddForm
template_name = 'phbook/form.html'
success_url = 'phbook:index'
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
print("Data is", form.instance)
model = form.save(commit=False)
print(model.org_name, model.org_details, model.primary_ph_number, model.secondary_ph_number)
model.save()
return self.get_success_url()
And the template form.html
{% extends 'base.html' %}
{% block head %}
<title>Add your Organisation's Entry | PhoneBook</title>
{% endblock %}
{% block body %}
{% load crispy_forms_tags %}
<form method="post" action="{% url 'phbook:add' %}" enctype="multipart/form-data">
<button type="submit" value="Submit">Submit</button>
{% csrf_token %}
{% crispy form %}
</form>
{% endblock %}
The base.html contains only the static links for the foundation css and js files.
In the EntryCreateView, when the line print("Data is ", form.instance) is executed it produced this result
Please tell me what I am doing here??
You've overridden post on the view, and are therefore bypassing all the calls to validation that the CreateView would normally do. There is rarely any good reason to override the get or post methods; you should always define a more specific method; in this case, form_valid would be more appropriate, if all you want to do is print out the submitted data. If that's just for debugging, then you may not need to override any methods at all.

Create a field in form for each model instance related to another instance in django

I'm trying to write an exam system using django. I have two models. Exam, Question.
Now I want to create form in which user will be able to answer the questions. So I want a field for every question. how can I create such a form?
UPD: Below is my code for Exam and Question models
from django.db import models
from django.utils import timezone
from django_jalali.db import models as jmodels
from django.utils.translation import ugettext as _
from django.core.exceptions import ValidationError
from users.models import Member
# Create your models here.
class Exam(models.Model):
name = models.CharField(max_length=500, verbose_name=_("Exam's name"))
start_date = models.DateTimeField(_("Start Date"))
end_date = models.DateTimeField(_("End Date"))
class Meta:
verbose_name = _("Exam")
verbose_name_plural = _("Exams")
def __unicode__(self):
return self.name
def stage(self):
#raise ValueError("%d", self.end_date, datetime.now())
if timezone.now() < self.start_date:
return -1 # exam hasn't started yet
elif timezone.now() >= self.end_date:
return 1 # exam has ended
else:
return 0 # exam is running
class Question(models.Model):
exam = models.ForeignKey(Exam, verbose_name=_("Related exam"))
order = models.IntegerField(unique=True,
verbose_name=_("Question's index"),
help_text=_("Questions will be shown based on their index. Also this index is shown as the question's number in exam page"))
statement = models.CharField(max_length=10000, verbose_name=_("Question's Statement"))
def __unicode__(self):
return self.exam.name + " - " + _("Question #") + str(self.order)
class Meta:
verbose_name = _("Question")
verbose_name_plural = _("Questions")
ordering = ['order']
You need to create a formset.
Formsets documentation
Creating formsets from models
Ok, I'll explain. First you should create Form with question (hidden) and answer (text) fields.
class AnswerForm(forms.Form):
question = forms.ModelChoiceField(queryset=Question.objects.all(),
widget=forms.HiddenInput)
answer = forms.CharField(required=True)
Next create formset with number of forms equal to the number of questions in exam. For each form initial question will be concrete exam's Question instance. Handling of the formset is standard as described in the documentation.
def pass_exam(request, exam_id):
exam = get_object_or_404(Exam, pk=exam_id)
questions = exam.question_set.all()
num_questions = questions.count()
AnswerFormSet = formset_factory(AnswerForm,
max_num=num_questions, validate_max=True,
min_num=num_questions, validate_min=True)
initial = [{'question': q} for q in questions]
if request.method == 'POST':
formset = AnswerFormSet(request.POST, initial=initial)
if formset.is_valid():
for form in formset:
question = form.initial['question']
answer = form.cleaned_data['answer']
# SAVE THE ANSWER HERE
return redirect('exam_done')
else:
formset = AnswerFormSet(initial=initial)
return render(request, 'app/pass_exam.html',
{'exam': exam, 'formset': formset})
Ans last - create pass_exam.html template for this formset:
<h1>{{ exam }}</h1>
<form action="." method="POST">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors }}
{% for form in formset %}
<p>
{{ form.initial.question.statement }}<br />
{{ form.question }}
{{ form.answer }}
{% if form.errors %}
{{ form.answer.errors }}
{{ form.question.errors }}
{% endif %}
</p>
{% endfor %}
<button type="submit">Submit</button>
</form>

Why does the parent form not show when I render the inline_formset factory?

I am attempting to use a class-based view to display information about a tool, and then add tags to that view, mostly to teach myself how to use inline_formsets. The problem that I am having is how to inject the child object's form into the template.
The problem is that the child's formsets are appearing, but there is no parent form displaying from the template.
The result is that the parent form doesn't show -
Ultimately, this is a "What am I doing wrong in Django?" question.
The model is very simple - Tool has a few fields that define my parent object, and tag is a child that is related to it:
models.py
from django.db import models
class Tool(models.Model):
content_id = models.CharField(
primary_key=True,
help_text="Confluence ID of the tool document",
max_length=12
)
tool_name = models.CharField(
max_length=64,
help_text="Short name by which the tool is called",
)
purpose = models.TextField(
help_text="A one sentence summary of the tools reason for use."
)
body = models.TextField(
help_text="The full content of the tool page"
)
last_updated_by = models.CharField(
max_length=64
)
last_updated_at = models.DateTimeField()
def __unicode__(self):
return u"%s content_id( %s )" % (self.tool_name, self.content_id)
class ToolTag(models.Model):
description = models.CharField(
max_length=32,
help_text="A tag describing the category of field. Multiple tags may describe a given tool.",
)
tool = models.ForeignKey(Tool)
def __unicode__(self):
return u"%s describes %s" % (self.description, self.tool)
I am using standard Class-based forms:
forms.py
from django.http import HttpResponseRedirect
from django.views.generic import CreateView
from django.views.generic import DetailView, UpdateView, ListView
from django.shortcuts import render
from .forms import ToolForm, TagsFormSet
from .models import Tool
TagsFormSet = inlineformset_factory(Tool, ToolTag, can_delete='True')
class ToolUpdateView(UpdateView):
template_name = 'tools/tool_update.html'
model = Tool
form_class = ToolForm
success_url = 'inventory/'
views.py
def call_update_view(request, pk):
form = ToolUpdateView.as_view()(request,pk=pk)
tag_form = TagsFormSet()
return render( request, "tools/tool_update.html",
{
'form': form,
'tag_form': tag_form,
'action': "Create"
}
)
And my template is as follows:
tool_update.html
{% block content %}
<form action="/update/" method="post">
{% csrf_token %}
<DIV>
Tool Form:
{{ form.as_p }}
</DIV>
<DIV>
Tag Form:
{{ tag_form.as_p }}
</DIV>
<input type="submit" value="Submit" />
</form>
{% endblock %}
This line:
form = ToolUpdateView.as_view()(request,pk=pk)
makes no sense at all. A view is not a form, and you can't use it as one. You should just do form = ToolForm(). Although note you also need some code to process the form submission itself, from the request.POST data.