Django: creating a text with gaps for input - python

I want to create a text on my website, where the gaps, in which the user can input a word (like the c-tests for learning language) are dynamically created depending on the text that the function gets from the database (not yet implemented).
My idea was it to create a formset in which each label is different depending on a variable I give it.
Here is my views.py
def ctest(request):
c_test_tokens, gaps, tokenindexe = generate_c_test(beispieltext())
# EXAMPLE
# NOT WORKING ON POST REQUEST YET
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = CTestform(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
ctestformset = formset_factory(CTestform, extra=len(gaps))
return render(request, 'ctest.html', {'form': ctestformset})
Here is my forms.py
class CTestform(forms.Form):
hello = forms.CharField(widget=forms.TextInput(attrs={'size': '5'}),
required=False, label='hello', label_suffix='')
Is this approach fine and how do I give the form a list with each element being the label for each gap, or should I search for an alternative?
EDIT:
Ok I tried the things on this site Variable number of inputs with Django forms possible?
My forms.py now has the function
def creategaps(self, c_test_tokens, gaps, tokenindexes):
wordsbeforegap = ''
gaps = {}
iteratorforgaps = 0
for i in (0, len(c_test_tokens)-1):
if '#GAP#' not in c_test_tokens[i]:
wordsbeforegap = wordsbeforegap + c_test_tokens[i]
else:
gaps[iteratorforgaps] = forms.CharField(widget=forms.TextInput(attrs={'size': '5'}),
required=False, label=wordsbeforegap, label_suffix='')
wordsbeforegap = ''
return type('test', (forms.BaseForm,), {'base_fields': gaps})
I tried to call the function from views.py but it doesnt create any form.
Here is the else statement in views.py
else:
form = CTestform.creategaps("test", c_test_tokens, gaps, tokenindexe)
return render(request, 'ctest.html', {'form': form})
Why doesnt it work?

Related

Combined Returns in Python Function Based Views Django

I'm currently building a website with Django and I've gotten to a point where I need to print data on the screen(ListView) and update(UpdateView) data on the same page(template). From what I've found I cant do this easily with the Django generic views so I rewrote my view as a function-based view. This current piece of code updates what I need perfectly with some changes to the HTML.
def DocPostNewView(request, pk):
context = {}
obj = get_object_or_404(DocPost, id=pk)
form = GeeksForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
return HttpResponseRedirect("/" + id)
context["form"] = form
posts = DocPost.objects.all()
return render(request, "my_app/use_template.html", context)
... And this following piece of code lists objects perfectly with some changes to the HTML.
def DocPostNewView(request, pk):
context = {}
obj = get_object_or_404(DocPost, id=pk)
form = GeeksForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
return HttpResponseRedirect("/" + id)
context["form"] = form
posts = DocPost.objects.all()
return render(request, 'my_app/use_template.html', context={'posts': posts})
I just need to combine these and the only real difference is the return command at the bottom of both(the same) functions. HOW CAN I COMBINE THE RETURNS SO I CAN LIST DATA AND UPDATE DATA ON THE SAME PAGE???
Thanks
does this work for you ?
def DocPostNewView(request, pk=None):
posts = DocPost.objects.all()
obj = get_object_or_404(DocPost, id=pk)
form = GeeksForm(request.POST or None, instance=obj)
if form.is_valid():
form.save()
return HttpResponseRedirect("/" + id)
context = {"form":form, "posts":posts}
return render(request, "my_app/use_template.html", context)
in your templates you could use "posts" to list every post,
and your can use "form" to update that specific post,
the thing is for every update you make you will always be seeing the whole list of posts, if it is that what you want to achieve

How to set default values in Forms

I am Building a BlogApp and i am stuck on a Problem.
What i am trying to do :-
I am trying to set the Default Value in forms.py for a Form.
views.py
def new_topic(request,user_id):
profiles = get_object_or_404(Profile,user_id=user_id)
if request.method != 'POST':
form = TopicForm()
else:
form = TopicForm(data=request.POST)
new_topic = form.save(commit=False)
new_topic.owner = profile
new_topic.save()
return redirect('mains:topics',user_id=user_id)
#Display a blank or invalid form.
context = {'form':form}
return render(request, 'mains/new_topic.html', context)
forms.py
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['topic_no','topic_title']
What have i tried :-
I also did by using initial , BUT this didn't work for me.
form = DairyForm(request.POST,request.FILES,initial={'topic_title': 'Hello World'})
The Problem
Default value is not showing when i open form in browser.
I don't know what to do
Any help would be appreciated.
Thank You in Advance
You need to pass initial in the GET method, like this:
if request.method == 'GET':
form = TopicForm(initial={'topic_title': 'Hello World'})
else:
More information can be found in documentation.
You have to use instance=TopicInstance If you want any specific instance to be default. Or you want any other initial you should pass it like this
def new_topic(request,user_id):
profiles = get_object_or_404(Profile,user_id=user_id)
if request.method != 'POST':
form = TopicForm(initial={'topic_title': 'Hello World'})#When displaying it for first time
else:
form = TopicForm(data=request.POST)
new_topic = form.save(commit=False)
new_topic.owner = profile
new_topic.save()
return redirect('mains:topics',user_id=user_id)
#Display a blank or invalid form.
context = {'form':form}
return render(request, 'mains/new_topic.html', context)

Formset: saving models won't work

Been trying to make this work all day. I have a main model which is entry. An entry can include several shifts. This is part of an app to store working hours. Here's the view for creating an entry:
class EntryCreateView(FormView):
template_name = 'entry/create.html'
form_class = AddWorkDay
success_url = reverse_lazy('time_manager:index')
def get(self, request, ordinal=None, *args, **kwargs):
""" Initiates with a blank form or will populate the day field with the day represented by the passed
ordinal. """
if ordinal:
day = datetime.datetime.fromordinal(int(ordinal))
form = AddWorkDay(initial={'day': day})
else:
form = AddWorkDay()
formset = ShiftFormSet()
return render(request, self.template_name, {'form': form, 'formset': formset})
def post(self, request, ordinal=None, *args, **kwargs):
form = AddWorkDay(data=request.POST)
formset = ShiftFormSet(data=request.POST)
errors = []
shifts = []
if form.is_valid() and formset.is_valid():
# Build entry.
entry = form.save(commit=False)
entry.owner = request.user
errors.extend(entry.validate(request.user))
# Build shift.
for form in formset:
shift = form.save(commit=False)
shift.entry = entry
shifts.append(shift)
errors.extend(shift.validate(request.user))
if len(errors) == 0:
entry.save()
for shift in shifts:
shift.save()
return HttpResponseRedirect(reverse('time_manager:index'))
return render(request, self.template_name, {'form': form, 'formset': formset, 'errors': errors, 'shifts': shifts, 'entry': entry})
When I try to enter an entry with a shift and press save, it terminates saying:
"IntegrityError at /time_manager/entry/create/
NOT NULL constraint failed: shift_shift.entry_id". I tried to figure out what was wrong with the shifts, so I commented the block out where the shift is saved (from "if len(errors)" to "return HttpResponseRedirect.") so that it would return to the view with the form. I then put {{ shifts }} into my template, to see what is in there. When I do that, it terminates with: "NoReverseMatch at /time_manager/entry/create/
Reverse for 'edit' with arguments '()' and keyword arguments '{'pk': None}' not found. 1 pattern(s) tried: ['time_manager/entry/shift/edit/(?P(\d+))/$']" as if I were trying to use the {% url %} tag referencing a view that doesn't exist. So I'm guessing that something goes wrong when I try to save the forms of the formset. However, I read the django documentation again and again and this is the way to store the forms of a formset, is it not?
Ok, with a fresh head I looked at my code, and then it occurred to me. I wanted to make sure that the entry and each shift are built correctly first, and then only save them if they don't violate any rules. So as you can see above: I'm saving both with commit=False. That means however that entry has not been assigned a primary key yet. The primary key is what the ForeignKeyField on my shift model needs. That's why Django failed saving it.
I changed the order of the method somewhat. This is the working code:
def post(self, request, ordinal=None, *args, **kwargs):
form = AddWorkDay(data=request.POST)
formset = ShiftFormSet(data=request.POST)
errors = []
shifts = []
if form.is_valid() and formset.is_valid():
# Build entry.
entry = form.save(commit=False)
entry.owner = request.user
errors.extend(entry.validate(request.user))
# Build shift.
for form in formset:
shift = form.save(commit=False)
shifts.append(shift)
errors.extend(shift.validate(request.user))
# If there are no errors, save the entry ans it's shifts.
if len(errors) == 0:
entry.save()
for shift in shifts:
shift.entry = entry
shift.save()
return HttpResponseRedirect(reverse('time_manager:index'))
return render(request, self.template_name, {'form': form, 'formset': formset, 'errors': errors, 'shifts': shifts, 'entry': entry})
Notice how entry is saved for the second time (without commit=False) and then assigned to shift.

POST doesnt work

Im trying to get the value form a post in django but it pass an empty field `def PersonEmail(request):
Im trying to get the value form a post in django but it pass an empty field `def PersonEmail(request):
if request.method == "POST":
form1 = PersonForm(request.POST, prefix="form1")
form2 = EmailForm(request.POST, prefix="form2")
name = form2['email'].value
return HttpResponse(name)
else:
form1 = PersonForm()
form2 = EmailForm()
return render(request, 'CreatePersonEmail.html', locals())`
but when i separate them i.e.
Im trying to get the value form a post in django but it pass an empty field `def PersonEmail(request):
if request.method == "POST":
# form1 = PersonForm(request.POST, prefix="form1")
form2 = EmailForm(request.POST, prefix="form2")
name = form2['email'].value
return HttpResponse(name)
else:
form1 = PersonForm()
form2 = EmailForm()
return render(request, 'CreatePersonEmail.html', locals())`
it gives me the value of the field.
Why? and how can i make it to obtain the values of both forms fields?
Basically, you're doing it wrong.
Firstly, you need to check if the form is valid. Users could type any crap in, you don't want to let them do that:
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
# Now you can access the fields:
name = form.cleaned_data['name']
If the form isn't valid, just pass it back to render() and it will show the errors.
Also, don't do this:
return render(request, 'CreatePersonEmail.html', locals())`
Build your context dictionary properly, don't use locals(), it's hacky and you pollute your context.
So a full view might look like this (taken from django docs and changed a bit:
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
name = form.cleaned_data['name']
return render(request, 'some_page.html', {'name': name})
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
You need to use the prefix both times you instantiate the forms; both on GET and on POST.
Also, you get values from the form's cleaned_data dict, not from the field.

How to pass data between django views

This questions addresses my question genearally, but I am looking for a more specific explanation.
I would like a user to update a a group of model objects, however, the queryset for these objects will need to be retrieved first. My plan is to do this in two seperate URs/views, getting the query set info from the first, then displaying the model formset to be updated next.
My first view gives a list of all the the "Project"s (One of my models), and retrieves the id of the project selected.
Here is the form:
class ProjectLookupForm(forms.Form):
Project_Name = chosenforms.ChosenModelChoiceField(queryset=Project.objects.all())
and here is the view:
def update_project_filter(request):
project_form = ProjectLookupForm(request.POST or None)
if request.method == 'POST':
if project_form.is_valid():
context = {"project_form":project_form}
# Get project here and share it with the next view.
selected_project_id = project_form.cleaned_data["Project_Name"].id
# Add a new return statement here?
# Or call update project view from here?
# Add a redirect button to html?
else:
errors = project_form.errors
context = {"errors":errors, "project_form":project_form}
else:
context = {"project_form":project_form}
return render(request, 'filter_update_project_form.html', context)
As one can see, I have included some comments brainstorming what my possibilities are. My goal is to send the selected_project_id to this next view, so that it can use that id as a model form query set.
def update_project(request):
UpdateFormset = modelformset_factory(Sample, fields=("sample_name", "extraction_date",
"project", "order", "notebook", "notebook_page"))
if request.method == 'POST':
formset = UpdateFormset(request.POST, request.FILES)
if formset.is_valid():
formset.save()
context = {"formset": formset, "project_form":project_form}
else:
errors = formset.errors
context = {"formset":formset, "errors":errors, "project_form":project_form}
else:
formset = UpdateFormset(queryset=Sample.objects.filter(project=2))
context = {"formset":formset, "project_form":project_form}
return render(request, 'update_project_form.html', context)
One can see here that I have hard coded the queryset like so:
queryset=Sample.objects.filter(project=2)
How can I set "project=" to my selected_project_id? Do I pass this info to the view as an input parameter? Or do I send it to the next URL and take it from there?
Assuming you've activated django.contrib.sessions.middleware.SessionMiddleware; you can pass data between views using request.session dictionary as follows:
def update_project_filter(request):
...
selected_project_id = project_form.cleaned_data["Project_Name"].id
request.session['selected_project_id'] = selected_project_id
...
def update_project(request):
...
selected_project_id = request.session.get('selected_project_id')
...

Categories