I have a form which contain a choicefield of items on my database.
My question is How can I get the selected value of my choicheField?
forms.py
class list_data(forms.Form):
message = forms.CharField(widget=forms.Textarea)
def __init__(self, author, *args, **kwargs):
super(list_data, self).__init__(*args, **kwargs)
self.fields['List'] = forms.ChoiceField(
choices=[(o.id, str(o)) for o in List.objects.filter(author=author)]
)
views.py
def sms(request):
form2 = list_data(author=request.user)
if request.method == "POST":
form2 = list_data(request.POST)
if form2.is_valid():
choice = form2.cleaned_data["List"]
print(choice)
else:
return render(request, "data_list/sms.html", {"form2": form2})
return render(request, "data_list/sms.html", {"form2": form2})
When I try to press the submit button it give me this error:
int() argument must be a string, a bytes-like object or a number, not 'QueryDict'
So I changed the form2 = list_data(request.POST) for form2 = list_data(author=request.user)
the error is gone but it print nothing else.
Thanks for helping
models.py
class List(models.Model):
item = models.CharField(max_length=100)
content = models.TextField()
site = models.CharField(max_length=11, choices=THE_SITE)
content_list = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.item
In case of a POST request, you pass request.POST as first parameter, and thus as author, and not as data. You can rewrite the view to:
def sms(request):
if request.method == 'POST':
form2 = list_data(request.user, data=request.POST)
if form2.is_valid():
choice = form2.cleaned_data["List"]
print(choice)
else:
form2 = list_data(author=request.user)
return render(request, "data_list/sms.html", {"form2": form2})
I would however advise to use a ModelChoiceField [Django-doc] here that will remove some boilerplate logic, and then you can work with model objects:
class ListDataForm(forms.Form):
message = forms.CharField(widget=forms.Textarea)
list = forms.ModelChoiceField(queryset=List.objects.none())
def __init__(self, author, *args, **kwargs):
super(list_data, self).__init__(*args, **kwargs)
self.fields['list'].queryset = List.objects.filter(author=author)
Note that according to the PEP-0008 style guidelines, the classes should be written in PerlCase (so ListDataForm, not list_data), and the attributes should be written in snake_case, so list, not List.
Related
I am trying to randomize a query set for a program i'm working on.
This is what's is in my views.py
full_quiz = Quiz.objects.get(name=quiz_name).questions.all()
form = TheForm(full_quiz)
if request.method == "POST":
form = QuestionForm(request.GET, full_quiz)
This is what is in my forms.py
class QuestionForm(forms.Form):
def __init__(self, questions, *args, **kwargs):
super(QuestionForm, self).__init__(*args, **kwargs)
for i in range(len(questions)):
if questions[i].answer.count() < 2:
self.fields["field_name %d" % i] = forms.CharField(label=questions[i], required=False)
From my testing, it appears like the query set is getting randomized with the second form variable. Any fix for this?
Thanks
You can get a random order queryset as described here:
full_quiz = Quiz.objects.get(name=quiz_name).questions.order_by('?')
You have not posted the code of your forms but I expect QuestionForm is a ModelFormSet. You can change the queryset of a ModelFormSet by setting the queryset arg (see docs):
form = QuestionForm(queryset=fullquiz)
Suppose you have a model structure like this
class Quiz(models.Model):
name = models.CharField('Quizname', max_length=200)
questions = models.ManyToManyField('Question')
class Answer(models.Model):
reply = models.CharField('Answer', max_length=100)
class Question(models.Model):
the_question = models.CharField('Question', max_length=100)
answers = models.ManyToManyField('Answer', related_name='answers')
correct_answer = models.ForeignKey('Answer', on_delete=models.CASCADE)
def is_correct(self, given_answer):
return given_answer == self.correct_answer
you could create a form structure with a ModelForm and a ModelFormSet:
class QuestionForm(forms.ModelForm):
myanswers = forms.ModelChoiceField(queryset=Answer.objects.all(), widget=forms.RadioSelect, required=False)
class Meta:
model = Question
fields = ('the_question', 'myanswers')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['myanswers'].queryset = self.instance.answers.all()
QuestionFormSet = modelformset_factory(Question, form=QuestionForm, extra=0)
And use it in a view:
def myview(request, quiz_name):
if request.method == 'POST':
formset = QuestionForm(request.POST)
if form.is_valid():
allanswers = [f['id'].is_correct(f['myanswers']) for f in formset.cleaned_data]
print('Number of correct answers:', sum(allanswers))
fullquiz = Quiz.objects.get(name=quiz_name).questions.order_by('?')
formset = QuestionForm(queryset=fullquiz)
return render(request, 'yourtemplate.html', {'formset': formset })
How can I write a url for a form using TemplateView. I wrote a method to validate and pass the company details through form. Using that form object that I'm trying to access the HTML fields.
Form.py
class CompanyDetailsForm(forms.Form):
class meta:
fields = ['company_name','contact_person','employee_count','email','mobile_number']
widgets = {
'comment':Textarea(attrs={'cols':30,'rows':5}),
}
company_name = forms.CharField(max_length=100,widget=forms.TextInput(attrs={'placeholder':'company Name'}))
contact_person = forms.CharField(max_length=100,widget=forms.TextInput(attrs={'placeholder':'Contact Person'}))
email = forms.CharField(max_length=100,widget=forms.TextInput(attrs={'placeholder':'Email'}))
employee_count = forms.CharField(max_length=100,widget=forms.TextInput(attrs={'placeholder':'Number Of Employee'}))
mobile_number = forms.CharField(max_length=100,widget=forms.TextInput(attrs={'placeholder':'Mobile Number'}))
View.py
class GetCompanyView(TemplateView):
template_name = "astra/company_details.html"
form = CompanyDetailsForm()
def get_context_data(self,**kwargs):
context = super().get_context_data(**kwargs)
context['form']=self.form
return context
def company_details(request):
if request.method =="POST":
form = CompanyDetailsForm(request.POST)
if form.is_valid():
company_name = form.cleaned_data['company_name']
contact_person = form.cleaned_data['contact_person']
email = form.cleaned_data['email']
employee_count = form.cleaned_data['employee_count']
mobile_number = form.cleaned_data['mobile_number']
try:
form.save()
send_mail(company_name,contact_person,email,employee_count,mobile_number,['salesastra500#gmail.com'])
except BadHeaderError:
return BadHeaderError
return render(request,'astra/company_details.html',{'form':form})
else:
return render(request,'astra/company_details.html')
I want to run my company_details.html file using TemplateView. I'm not able to write the url for same. Plz suggest
TemplateView only have get method
def get(self, request, *args, **kwargs):
return render(request,self.template_name, {'form': self.form})
if you have get and post methods use FormView
I am using bootstrap-modal-forms to show a user a formset with some inline forms. It is possible for the user to save the form if data is only entered into the original form, but if the inline formset has data then I get the following error:
'NoneType' object has no attribute 'is_ajax'
The inline formset was working correctly before I tried to implement them in the modal form. The problem seems to arise only when the inline formset (projectimages) is saved it is a NoneType.
My views.py
class ProjectCreate(BSModalCreateView):
form_class = ProjectForm
template_name = 'project_form.html'
success_message = 'Success: %(project_name)s was created.'
def get_success_url(self):
return reverse_lazy('project-detail', kwargs={'project': self.object.slug})
def get_context_data(self, **kwargs):
data = super(ProjectCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['projectimages'] = ProjectFormSet(self.request.POST, self.request.FILES,)
else:
data['projectimages'] = ProjectFormSet()
return data
def form_valid(self, form):
form.instance.date_created = timezone.now()
context = self.get_context_data()
projectimages = context['projectimages']
with transaction.atomic():
self.object = form.save()
if projectimages.is_valid():
projectimages.instance = self.object
projectimages.save()
return super(ProjectCreate, self).form_valid(form)
My forms.py
class ProjectForm(BSModalForm):
class Meta:
model = Project
exclude = ['date_created', 'slug']
ProjectFormSet = inlineformset_factory(
Project,
ProjectImage,
can_delete=True,
form=ProjectForm,
extra=1,
)
My models.py
class Project(models.Model):
project_name = models.CharField(max_length=100)
date_created = models.DateTimeField('Created on')
slug = models.SlugField(unique=True)
def __str__(self):
return self.project_name
def save(self, *args, **kwargs):
self.slug = slugify(str(self))
super(Project, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('project-list')
class ProjectImage(models.Model):
image = models.ImageField(verbose_name='Additional Images', upload_to=project_directory_path)
project = models.ForeignKey(Project, on_delete=models.PROTECT)
annotation = models.CharField(max_length=200, blank=True)
I expect the user to be able to add as many images to the modal formset as they like.
The BSModalForm expects you to initialise it with the request. This happens in your BSModalCreateView for the main form but not for your formset, because you initialise it manually.
So when initialising, just add the form_kwargs attribute:
if self.request.POST:
data['projectimages'] = ProjectFormSet(
self.request.POST, self.request.FILES,
form_kwargs={'request': self.request})
else:
data['projectimages'] = ProjectFormSet(form_kwargs={'request': self.request})
Note that I think the form you set in ProjectFormSet is wrong, because it should be a form for a ProjectImage model, not a Project. It should actually be called ProjectImageFormSet to better reflect what it is.
You probably want to remove form=ProjectForm as it probably doesn't need to be a BSModalForm (not sure about that). In that case you should not pass the request in form_kwargs. If not, you just need to create another ProjectImageForm class.
Finally, you should not return super().form_valid() because that will save the main form a second time (you already did). Do the redirect yourself.
I am developing a django application which has a form for creating ingredients. The form contains a dropdown for selecting Recipes. When a user creates an ingredient, in the dropdown, I want that only those recipes should appear that are created by the same user.
Here is my code:
#forms.py
class IngredientForm(forms.ModelForm):
primal = forms.BooleanField()
class Meta:
model = Ingredient
fields = ('recipe_id', 'title', 'instructions', 'rules')
#models.py
class Recipe(models.Model):
user = models.ForeignKey('auth.User')
title = models.CharField(max_length=500)
description = models.TextField(max_length=500)
rules = models.TextField(max_length=500,blank=True)
def __str__(self):
return self.title
class Ingredient(models.Model):
user = models.ForeignKey('auth.User')
recipe_id = models.ForeignKey(Recipe, on_delete=models.CASCADE)
title = models.CharField(max_length=500)
instructions = models.CharField(max_length=500)
rules = models.TextField(max_length=500,blank=True)
primal = models.CharField(default='0',max_length=500,blank=True)
def __str__(self):
return self.title
#views.py
def create_ingredient(request):
if request.method == 'POST':
form = IngredientForm(request.POST)
if form.is_valid():
current_user = request.user
data = form.cleaned_data
ingredient_data=Ingredient.objects.create(user=current_user, recipe_id=data['recipe_id'],title=data['title'], primal=data['primal'], instructions=data['instructions'], rules=data['rules'])
ingredient_data.save()
ingredient = Ingredient.objects.get(pk = ingredient_data.pk)
return redirect('ingredient_detail', pk=ingredient.pk)
else:
messages.error(request, "Error")
return render(request, 'create_ingredient.html', {'form': IngredientForm })
The problem is that right now, when the user tries to select a recipe, the recipes created by all users of the site appear in the 'recipe_id' dropdown. He should only be able to see recipes in the dropdown that are created by himself. Any ideas how to do it?
UPDATE FROM ANSWER:
If I use this:
...
if request.method == 'POST':
form = IngredientForm(current_user=request.user, request.POST)
if form.is_valid():
...
it gives me this syntax error: non-keyword arg after keyword arg in this line form = IngredientForm(current_user=request.user, request.POST)
UPDATE#2:
If I use:
...
if request.method == 'POST':
form = IngredientForm( request.POST,current_user=request.user)
if form.is_valid():
...
It gives me error: __init__() got multiple values of argument 'current.user'
If I use:
...
if request.method == 'POST':
form = IngredientForm( request.POST)
if form.is_valid():
...
It gives me error: 'QueryDict' object has no attribute 'id'
UPDATE # 3:
After implementing the latest update from answer. It gives me error name 'current_user' is not defined
in the following piece of code:
def create_ingredient(request):
form = IngredientForm(current_user=request.user)
In the model form you can do this:
class IngredientForm(ModelForm):
primal = forms.BooleanField()
class Meta:
model = Ingredient
fields = ('recipe_id', 'title', 'instructions', 'rules')
def __init__(self, current_user, *args, **kwargs):
super(IngredientForm, self).__init__(*args, **kwargs)
self.fields['recipe_id'].queryset = self.fields['recipe_id'].queryset.filter(user=current_user.id)
then instantiate the form like so
form = IngredientForm(current_user=request.user)
EDIT #1:
Passing in the user to the POST request form:
if request.method == "POST":
form = IngredientForm(request.POST, current_user=request.user)
if form.is_valid():
....
EDIT #2:
Try changing the init decleration to what is below and pop the user from the kwargs:
def __init__(self, *args, **kwargs):
current_user = kwargs.pop('current_user', None)
super(IngredientForm, self).__init__(*args, **kwargs)
if current_user:
self.fields['recipe_id'].queryset = self.fields['recipe_id'].queryset.filter(user=current_user.id)
I think this might solve your problems, leave the rest of the code the same as my answer above (where you create the forms)
I am trying to show logged in users a form where they can input name and sensitivity, however this needs to be associated with the logged in user (currently attempting this with the foreign key and init). This is my approach:
Views:
#login_required
def restricted(request):
context = RequestContext(request)
if request.method == 'POST':
form = TrackForm(request.user, request.POST)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print form.errors
else:
form = TrackForm()
return render_to_response('tracker/restricted.html', {'form': form}, context)
Forms:
class TrackForm(forms.ModelForm):
name = forms.CharField(help_text="Please enter a name")
sensitivity = forms.IntegerField(help_text="Sensitivity: 1=Low Sensitivity, 2=Standard Sensitivity, 3=Highly Sensitive", initial=0)
def __init__(self, user, *args, **kwargs):
self.user = user
super(TrackForm, self).__init__(*args, **kwargs)
class Meta:
model = ToTrack
fields = ('name','sensitivity')
Models:
class ToTrack(models.Model):
name = models.CharField(max_length=128)
sensitivity = models.IntegerField(default=0)
user = models.ForeignKey(User)
def __unicode__(self):
return self.name
I currently get the error message:
TypeError at /tracker/restricted/
__init__() takes at least 2 arguments (1 given)
change views.py like this(I comment changed line):
#login_required
def restricted(request):
context = RequestContext(request)
if request.method == 'POST':
form = TrackForm(request.POST, user=request.user) #this line change
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print form.errors
else:
form = TrackForm(user=request.user) #this line change
return render_to_response('tracker/restricted.html', {'form': form}, context)
And change form like this:
class TrackForm(forms.ModelForm):
name = forms.CharField(help_text="Please enter a name")
sensitivity = forms.IntegerField(help_text="Sensitivity: 1=Low Sensitivity, 2=Standard Sensitivity, 3=Highly Sensitive", initial=0)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
if user:
self.user = user
super(TrackForm, self).__init__(*args, **kwargs)
class Meta:
model = ToTrack
fields = ('name','sensitivity')