django-forms-builder. How do I update a form? - python

I am using django-forms-builder in my project and there is just one thing i cant get my head around. How do i set up an update form to edit a filled out form?
Here is my attempt at making an update form for django-forms-builder
urls.py
url('forms/update/(?P<pk>\d+)/$', FormUpdateView.as_view(),
name='form-update'),
views.py
class FormUpdateView(UpdateView):
model = FieldEntry
template_name = 'form/update_form.html'
form_class = FormForForm
success_url = '/assessments/all/'
update-form.py
{% render_built_form id=form_instance.id %}

I haven't used this software yet but it could fill a spot in my arsenal so I took a stab at it. I'm not using CBV because I'm still just poking around. This is the get part only, I can expand on how to handle the post part (and preferably do it with CBV) on the weekend. It's relatively straightforward to implement post with view functions (if request.METHOD == "POST":#update the row). The render is of course just plain raw HTML but forms_builder doesn't offer any styling input. You probably want to tack the user onto the FormEntry, I don't have a good idea about authorization yet. I'll give it some thought and amend the answer.
views.py
from forms_builder.forms.models import Form
def get_form(request, slug, pk):
form_obj = Form.objects.get(slug=slug)
form_instance = form_obj.entries.get(pk=pk)
form = FormForForm(form=form_obj, instance=form_instance, context=request)
return render_to_response('update_form.html', {'form':form_instance.form, 'request':request})
templates/update_form.html
{{ form.as_p }}

Related

How can I create a submit form in Django with a dropping down list?

I am just starting to work with Django and I have some problems with forms and dropping lists.
I have a model with two attributes, and I want to display one of the attributes in a dropping down list (this one will be unchangeable) and another one in a text field (this one will be changeable). Also, I have a submit button, so I want to change a second attribute in a text field and by pressing on the button. How can I do this? What would some examples be?
As you are starting to work with Django, you might or might not know about how Django handle forms.
In Django, forms can be handled in two ways:
User-created and managed forms (without any form class)
Class-managed forms (connected to Django models)
Documentation form Django Forms
Now let’s talk about the first type of forms (where you create your HTML form and manage the request sent to server):
These forms are simple to make and when there are only a few and are only suggested when you have a very small amount of inputs (I would say four or fewer inputs).
Here is a simple example of subscription of a newsletter with an email example.
<form id='sub-form' method="POST">
{% csrf_token %}
<div>
<input type="email" name="sub_email">
</div>
<input class="button" value="Subscribe" type="submit" id="subbutton">
</form>
So a very important thing to look at here is {% csrf_token %}, about which you can read more about here and about how it works and prevents cross-site request forgery. This token will be required when you make a request to Django server with any post request and data.
In this subscription form you see one <input> with name="sub_email". Take note of this as we will use this to get this value on the server as this is the key to its value, and then a simple Submit Button.
When you press Submit on a page let’s say url = "http://BASE_URL/home" you will receive a POST request on the view that handles that URL.
So now coming to the view.py, let’s say you only allow registered users to subscribe then the view will go something like this (assuming you are not expecting any other request from the home URL).
def home(request):
user=request.user
if request.method == "POST":
if user.is_authenticated:
email = request.POST['sub_email'] #Using name of input
#Logic to save this email
return HttpResponse("You are Subscribed",status=200)
else:
return HttpReposnse("You are not Authenticated",status=401)
else:
return render(request,"home.html")
Now as you are the expert of simple forms, let’s work with Django class-based forms.
These views are a little work when you have very few inputs, but they are a great help in manageability and when you have to work with large number of inputs.
You will request these Class Based Forms as in your question you are trying to send an instance of a model from your Models.py to a form to user.
I have a model of Posts that can be used for this example:
class Post(models.Model):
postTitle = models.CharField(max_length = 90,null=True)
subTitle = models.CharField(max_length = 160,null=True)
location = models.CharField(max_length = 3,default = 'IN',null=True)
Now according to your question, you are trying to let the user change one attribute, let’s say postTitle and for location you are not letting the user select one of the countries which is preselected and for your post.
Now we have to create a form for this. Forms in class based are created in Forms.py. If you don't have forms.py then you can create one right along models.py and views.py.
Now for the form, I would like to edit some existing data as you are saying one of the attributes (Fields) is fixed and another editable, but you get the value from the model.
class PostEditForm(ModelForm):
location = forms.CharField(label='Country ',widget=forms.Select(attrs={'class': 'Classes_HERE','placeholder':' Select a Country','disabled':'disabled'} ,choices=country_list),required=True)
class Meta:
model = Post
fields= ['postTitle','subTitle','location']
labels = {
'postTitle':'Title',
'subTitle':'Sub-Title',
}
widgets = {
'postTitle': forms.TextInput(attrs={'class': 'mention_class_here','placeholder':' Add Title'}),
'subTitle': forms.TextInput(attrs={'class': 'mention_class_here','placeholder':' Add Sub-Title'})
}
Attributes can be mentioned in forms fields the way I have mentioned them in the above example. I used disabled="disabled" to disable (not editable) location field and used forms.Select to make it drop down.
You might also see that I gave the location field a list to choose from. This is how you can create a list of your items. It's been quite some time when I wrote this, so there might be errors or it may not work for you, so just make sure you are referring to the current documentation and searching Stack Overflow for answers.
country_list = [
('', 'Select a Country'),
("AF", "Afghanistan"),
("AX", "Aland Islands"),
("AL", "Albania"),
("DZ", "Algeria"),
("AS", "American Samoa"),
("AD", "Andorra"),
("AO", "Angola"),
("AI", "Anguilla"),
("AQ", "Antarctica"),
("AG", "Antigua And Barbuda"),
("AR", "Argentina"),
("AM", "Armenia"),
("AW", "Aruba"),
.
.
.
Now this form can be passed as context in a view to an HTML page.
def editPost(request,post_id):
user=request.user
post = get_object_or_404(Post,id=post_id) #Getting the instance of Post
if user.is_authenticated:
formPost = PostEditForm(request.POST or None,instance=post)
if request.method=='POST':
if formPost.is_valid():
savedPost=formPost.save()
else:
return render(request,'postEdit.html',{'formPost':formPost})
else:
return HttpResponse("Not Authorized",status:401)
Now your HTML file postEdit.html should look something like this:
<form id="post-form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div>
{{formPost}}
</div>
</form>
That is it and adding a submit button in the same form, you can now edit your instance of post that you passed along with this {{formPost}}. Combine your logic wherever you think needs a change to fit in what you want to do.
By no means I am saying all this code is in working condition, but it is shown only to illustrate the flow and working.

How do I connect forms to models in Django?

I've just started learning Django, and I have questions regarding forms and models.
So what I'm trying to create, in simplified feature, is a user inputs his/her basic information--phone #, instagram account, facebook account, and so on--then the data is stored in database and show up dynamically to the user. Just like social media. But I'm having confusion with forms and models.
What I first did was create forms, like below (forms.py) :
from django import forms
from django.contrib.auth.models import User
class InputUserInfo(forms.Form):
phone = forms.CharField(max_length=20)
instagram = forms.CharField(max_length=20)
facebook = forms.CharField(max_length=20)
# and so on. Don't mind about which field to use.
then I have my views.py file, written as below:
from django.shortcuts import render, redirect
from .forms import InputUserInfo
def inputuserinfo(response):
if response.method == "POST":
form = InputUserInfo(response.POST)
if form.is_valid:
form.save()
return redirect('home')
else:
form = InputUserInfo()
return render(response, 'inputuserinfo.html', {'form' : form }
then I have my inputuserinfo.html file, like below:
{% extends 'base.html' %}
{% block content %}
<form method="post" action='/inputuserinfo/'>
{% csrf_token %}
{{form}}
<button type='submit'>Done</button>
</form>
{% endblock%}
Now the problem is, I don't know what to do with my models.py. I don't know which code to write in models.py to store the input data into my database.
I would very much appreciate your help guys. :)
it looks like you left out the ModelForm. Is there any reason you arent using Class Based Views. This would be much easier?
Your ModelForm will end up looking something like this
class InputUserForm(forms.ModelForm):
'''user form to create the user profile'''
class Meta:
model = InputUserInfo
fields = '__all__'
In Django, a model is an interface to a particular database. It will create and issue SQL queries for you.
A form is an interface to HTTP "GET" and "POST." It can generate the HTML to insert into a form-tag (but do not provide the tag itself), and they can interpret the data that is presented to the host from such a form. Instead of monkeying around with the HTML data yourself, you let the Form object do the heavy lifting, both coming and going.
Django provides many convenience shortcuts, such as ModelForm, which is a Form that can be based on the content of a Model. It will "quickly produce an acceptable-looking form" when you are in a hurry ... as you often are. And it can do things like save() directly because it knows what model you want to save the data to.

Django CreateView with get_success_url not working for this specific case

I'm using Django 2.1.
I'm having a problem with a CreateView because I need to redirect to the update url, but that url contains one argument that is created manually after verifying that the form is valid.
This is the view code:
class ProjectCreateInvestmentCampaignView(LoginRequiredMixin, SuccessMessageMixin, generic.CreateView):
template_name = 'webplatform/project_edit_investment_campaign.html'
model = InvestmentCampaign
form_class = CreateInvestmentCampaignForm
success_message = 'Investment campaign created!'
def get_success_url(self):
return reverse_lazy('project-update-investment-campaign',
args=(self.kwargs['pk'], self.object.campaign.pk, self.object.pk))
def form_valid(self, form):
project = Project.objects.get(pk=self.kwargs['pk'])
form.instance.investment_type = "A"
form.instance.contract_type = "CI"
form.instance.history_change_reason = 'Investment campaign created'
valid = super(ProjectCreateInvestmentCampaignView, self).form_valid(form)
if valid:
campaign = CampaignBase.objects.create(project=project, )
form.instance.campaign = campaign
form.instance.campaign.project = project
form.instance.campaign.creation_date = timezone.now()
form.save()
return valid
As you can see, on the form_valid I validate first the form, and then I create the object campaign and assign all the related data. This is working fine.
The problem came when I changed the get_success_url to fit my use case, that is redirecting to the update view.
I debugged and saw that at the moment I create the variable valid on the form_valid, it checks the success url, and that triggers me the following error:
Exception Type: AttributeError
Exception Value:
'NoneType' object has no attribute 'pk'
Exception Location: /Volumes/Archivos/work/i4b/webplatform/views/investor_campaign_views.py in get_success_url, line 25
I asume that the error is because the campaign is not created yet so it's trying to get the pk from a non existing object.
The thing is that I cannot create the campaign if the form is not validated, but I need the campaign to make the url working (that url is working as it is on the UpdateView that I already have).
It will only invoke get_success_url after form_valid. So it's up to form_valid to create and save the objects needed. If it's valid for them not to be created, you need a different approach. Maybe initialize (say) self.campaign_pk = 0, update it if a campaign can be created with the pk of the campaign object, and let the next view sort out what to do when pk==0. Or,
...
args=(self.kwargs['pk'],
self.object.campaign.pk if self.object.campaign else 0,
self.object.pk))
(I don't fully follow your code so I might be barking up the wrong tree here)
It may be that you don't want CreateView but FormView, which doesn't handle object creation for you, so you may find greater flexibility over how to process a valid form that nevertheless cannot be fully honoured all the time. Or even, just a plain old function-based view, in which you can process two or more forms and be far more able to decide on conditions that constitute non-validity even after all the forms have technically validated.
This is a function-based view structure I have used where I have two forms to process, and a fairly long but boring set of operations to do after BOTH forms validate:
def receive_view( request):
# let's put form instantiation in one place not two, and reverse the usual test. This
# makes for a much nicer layout with actions not sandwiched by "boilerplate"
# note any([ ]) forces invocation of both .is_valid() methods
# so errors in second form get shown even in presence of errors in first
args = [request.POST, ] if request.method == "POST" else []
batchform = CreateUncWaferBatchForm( *args, layout=CreateUncWaferBatchLayout )
po_form = CreateUncWaferPOForm( *args, layout = CreateUncWaferPOLayout, prefix='po')
if request.method != "POST" or any(
[ not batchform.is_valid(), not po_form.is_valid() ]):
return render(request, 'wafers/receive_uncoated.html', # can get this out of the way at the top
{'batchform': batchform,
'po_form': po_form,
})
#it's a POST, everything is valid, do the work
...
return redirect('appname:viewname', ...)
For me, get_success_url was not invoked as the form was not valid (was invalid) and I didn't know. You can override form_invalid(self, form) to control the behavior.
Also, consider this block of code to show any errors in your template
{% if form.errors %}
<div class="alert alert-danger" role="alert">
{% for field, errors in form.errors.items %}
{% for error in errors %}
<b>{{ field }}</b>: {{ error }}
{% endfor %}
{% endfor %}
</div>
{% endif %}

How to have 2 Models/Views/Templates on one Page in Django?

I know this has probably been asked a 1000 times but I still for the life of me can't find a decent answer. Reading tutorials, Stack and even looking at GitHub for examples - nothing is helping or even guiding me in the right direction.
I have 2 separate Models/Views/Templates which each work beautifully on their own URLS.
I want these Models/Views/Templates to work on a single URL ie
url(r'^$', 'chrometask.views.index', name='home'),
(ie my homepage.)
How??? This seems to be way overly complicated for such a simple ask.
here is my views.py
items = Task.objects.all()
form = TaskForm(request.POST or None)
task_list = Task.objects.order_by('priority')
context_dict = { 'Tasks' : task_list}
if form.is_valid():
save_it = form.save(commit=False)
save_it.save()
return HttpResponseRedirect('')
return render_to_response('home.html', #Don't know how to render 2 html files or add the context_dict
locals(),
context_instance=RequestContext(request))
render_to_resonse can only take 3 arguments, and when I put the table inside 'home.html' with {% blockquote %} it wont show on the page.
<div class="collapsible-header"><i class="mdi-hardware-laptop-"></i>Title</a></div>
<div class="collapsible-body">
<div class="intro grey lighten-5">
{% block content %}{% endblock %} <--This is the table.html, which extends fine with {% entends 'home.html' %}-->
</br>
Please don't direct me to tutorials, These haven't resolved the issue, I would rather you spelt it out in layman's terms if possible, this may help drill the answer into my thick skull.
note - I am New to Django.
(*apologies for the frankness - this is really beginning to drive me up the wall)
You haven't said what you have tried to do to solve this, why you think it's "way overly complicated", or why it's causing you frustration.
The thing to understand is that a view is entirely responsible for a page, ie the view creates the entire response when a URL is requested. You can't have two views on a URL; that simply doesn't make sense.
But the other thing to understand is that templates and models are not in any way connected to any particular view. One view can render any number of templates, one, many, or even none; and any of those templates can involve any number of models from any number of apps.
So, if you wanted to include tasks in your home page, one approach is just to include the Tasks in the context dictionary in your view, and include the relevant code to your template to display them.
Now, if you're doing this in multiple views, then clearly you want to put that code somewhere sensible so that you Don't Repeat Yourself. The ideal place for this is an inclusion tag: you can wrap up the code that queries the latest tasks, and the template fragment that renders them, into a single tag which you can then use on your home page template.
Edit
The context is the second argument to render_to_response: the place where you've currently put locals(). That's a horrible pattern which I hate, and which is confusing you unnecessarily. Instead of using locals, put all the things you require for the context specifically into context_dict and pass that there instead.
(Also note you should use the render shortcut rather than render_to_response, which accepts the request as the first parameter and doesn't need all that mucking around with RequestContext.)
context_dict = { 'Tasks' : task_list, 'form': form}
...
return render(request, 'home.html', context_dict)
The code you have posted for your template is confusing. Is the table in table.html or home.html? If you pass "home.html" in your call to render, then it's home.html that will be rendered, not table. If you want to render table so that it extends home, then pass "table.html" in your call to render.
My Code ended up looking like
def home(request):
items = Task.objects.all()
form = TaskForm(request.POST or None)
task_list = Task.objects.order_by('priority')
context_dict = { 'Tasks' : task_list, 'form': form}
if form.is_valid():
save_it = form.save()
save_it.save()
return HttpResponseRedirect('')
return render(request, 'table.html', context_dict)
This has both models workig in one view.
def tutlist(request,tut):
model = Tutorial
model = Codetut
template_name = 'backpages/tutorial.html'
temp = Parts.objects.get(pk = tut)
model = temp.tutorial_set.all()
print(model)
temp = Parts.objects.get(pk = tut)
code = temp.codetut_set.all()
print(code)
context = {'model' : model ,'something': code}
return render(request, template_name,context)
i was having a similar problem i wanted to display two different views on a single template with a single url so i did something like this which worked well for me.

Django formsets: make first required?

These formsets are exhibiting exactly the opposite behavior that I want.
My view is set up like this:
def post(request): # TODO: handle vehicle formset
VehicleFormSetFactory = formset_factory(VehicleForm, extra=1)
if request.POST:
vehicles_formset = VehicleFormSetFactory(request.POST)
else:
vehicles_formset = VehicleFormSetFactory()
And my template looks like this:
<div id="vehicle_forms">
{{ vehicles_formset.management_form }}
{% for form in vehicles_formset.forms %}
<h4>Vehicle {{forloop.counter}}</h4>
<table>
{% include "form.html" %}
</table>
{% endfor %}
</div>
That way it initially generates only 1 form, like I want. But I want that one form to be required!
When I dynamically add blank forms with JavaScript and vehicles_formset.empty_form all those extra forms are required, which I don't want.
From the docs:
The formset is smart enough to ignore extra forms that were not changed.
This is the behavior the first form is exhibiting (not what I want) but not the behavior that the extra forms are exhibiting (what I do want).
Is there some attribute I can can change to at least make one form required?
Found a better solution:
class RequiredFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
super(RequiredFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
Then create your formset like this:
MyFormSet = formset_factory(MyForm, formset=RequiredFormSet)
I really don't know why this wasn't an option to begin with... but, whatever. It only took a few hours of my life to figure out.
This will make all the forms required. You could make just the first one required by setting self.forms[0].empty_permitted to False.
New in Django 1.7: you can specify this behaviour with your formset_factory
https://docs.djangoproject.com/en/1.8/topics/forms/formsets/#validate-min
VehicleFormSetFactory = formset_factory(VehicleForm, min_num=1, validate_min=True, extra=1)
Well... this makes the first form required.
class RequiredFormSet(BaseFormSet):
def clean(self):
if any(self.errors):
return
if not self.forms[0].has_changed():
raise forms.ValidationError('Please add at least one vehicle.')
Only "problem" is that if there are 0 forms, then the clean method doesn't seem to get called at all, so I don't know how to check if there are 0. Really...this should never happen though (except that my JS has a bug in it, allowing you to remove all the forms).
Oh I think I see. Try this:
from django.forms.formsets import BaseFormSet, formset_factory
class OneExtraRequiredFormSet(BaseFormSet):
def initial_form_count(self):
return max(super(OneExtraRequiredFormSet,self).initial_form_count() - 1,0)
VehicleFormSetFactory = formset_factory(VehicleForm, formset=OneExtraRequiredFormSet, extra=1)
== Original answer below ==
When you say "at least make one form required", I assume you mean "make only one extra form required, regardless of how many have been added via javascript".
You will need to have hidden input on your page which contains the number of forms that have been added via javascript, and then use that number, minus 1, as the value to pass in as the extra attribute to your formsets constructor.

Categories

Resources