Passing Parameters to Django CreateView - django

I am trying to implement an appointment-making application where users can create sessions that are associated with pre-existing classes. What I am trying to do is use a django CreateView to create a session without asking the user for an associated class, while under the hood assigning a class to the session. I am trying to do this by passing in the pk of the class in the url, so that I can look up the class within the CreateView and assign the class to the session.
What I can't figure out is how exactly to do this. I'm guessing that in the template I want to have something like <a href="{% url create_sessions %}?class={{ object.pk }}>Create Session</a> within a DetailView for the class, and a url in my urls.py file containing the line
url(r'^create-sessions?class=(\d+)/$', CreateSessionsView.as_view(), name = 'create_sessions'), but I'm pretty new to django and don't exactly understand where this parameter is sent to my CBV and how to make use of it.
My plan for saving the class to the session is by overriding form_valid in my CBV to be:
def form_valid(self, form):
form.instance.event = event
return super(CreateSessionsView, self).form_valid(form)
If this is blatantly incorrect please let me know, as well.
Thank you!

GET parameters (those after ?) are not part of the URL and aren't matched in urls.py: you would get that from the request.GET dict. But it's much better to make that parameter part of the URL itself, so it would have the format "/create-sessions/1/".
So the urlconf would be:
url(r'^create-sessions/(?P<class>\d+)/$', CreateSessionsView.as_view(), name='create_sessions')
and the link can now be:
Create Session
and now in form_valid you can do:
event = Event.objects.get(pk=self.kwargs['class'])

urls.py
path('submit/request/<str:tracking_id>', OrderCancellationRequest.as_view(), name="cancel_my_order"),
Template
<form method="POST">
{% csrf_token %}
{{form | crispy}}
<button class="btn" type="submit">Submit</button>
</form>
View
class MyView(CreateView):
template_name = 'submit_request.html'
form_class = MyForm
model = MyModel
def form_valid(self, form, **kwargs):
self.object = form.save(commit=False)
self.object.created_at = datetime.datetime.now()
self.object.created_for = self.kwargs.get('order_id')
self.object.submitted_by = self.request.user.email
super(MyView, self).form_valid(form)
return HttpResponse("iam submitted")
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['header_text'] = "My Form"
context['tracking_id'] = self.kwargs.get('order_id')
return context

Related

Django templates not updating after database changes via admin

I am using Django v2.2 admin to change the information on my database but after I change it and refresh the page, the new data is not there, only the old data.
A fix for this if I restart the server, the templates can now fetch the new data that I input.
views.py
# template with context
class Home(TemplateView):
template = 'home.html'
context = { 'bar': Baby.objects.all() }
def get(self, request):
return render(request, self.template, self.context)
home.html
{% for foo in bar %}
{{ foo.name }}
{{ foo.cost }}
{% endfor %}
How I can get the new data by refreshing the page and not restarting the server?
As others mentioned, use get_context_data() method is good idea, because ContextMixin is parent class (not base class, but part of TemplateView's __mro__ Method Resolution Order) of TemplateView which is responsible to pass data from view to template. But, if you want to render template manually using get() method, You should hit on database on every GET request (in your case).
class Home(TemplateView):
template = 'home.html'
def get(self, request):
self.context = {'bar': Baby.objects.all()}
return render(request, self.template, self.context)
Your code does not work, because static variables are initialized only once. In your case context was static variable.
Hope, it helps you.
Can you please try this?
class Home(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['bar'] = Baby.objects.all()
return context

Auto-populate Django template with context variable passed from a Django view (no js)

I have Django template which has one CharField. Users can enter a mobile phone number here and submit it.
The view connected to this template sometimes passes to the said template, a context variable containing a pre-defined phone number {{ number }}.
How do I auto-populate the CharField in the Django template with {{ number }}?
The template code is like so:
<form action="{% url 'user_phonenumber' slug=unique num=decision %}" method="POST">
{% csrf_token %}
<b style="font-size:90%;color:green;">Enter your mobile number:</b><br><br>
{{ form.mobile_number }}<br><br>
<input type="submit" name="number" value="OK"><br>
</form>
If required, forms.py contains the following:
class UserPhoneNumberForm(forms.Form):
mobile_number = forms.CharField(max_length=50)
class Meta:
fields = ("mobile_number")
In views.py, I simply have:
class UserPhoneNumberView(FormView):
form_class = UserPhoneNumberForm
template_name = "get_user_phonenumber.html"
def get_context_data(self, **kwargs):
context = super(UserPhoneNumberView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
#some code to determine what number should be
context["number"] = number
return context
I've looked at a few other SO answers. The last one I've linked comes close to solving the problem, but the solution isn't for dynamic values (which is what I need), plus that's for modelforms. The rest don't quite address my particular needs.
I needed to over-ride def get_initial(self): of my FormView to achieve this. I.e. instead of passing a context variable to the template, pass the variable as initial through get_initial() to the related form.
Here's how I did it:
def get_initial(self):
"""
Returns the initial data to use for forms on this view.
"""
if self.request.user.is_authenticated():
#What's the latest chatpicmessage which houses a pic you own
try:
msg = PicMessage.objects.filter(which_pic__owner=self.request.user).latest('sending_time')
self.initial = {'mobile_number': msg.what_number} #initial needs to be passed a dictionary
return self.initial
except:
return self.initial
else:#i.e user is not authenticated
return self.initial
return self.initial
More documentation regarding this can be found here: https://docs.djangoproject.com/es/1.9/ref/forms/fields/#initial

'str' object has no attribute 'visible_fields'; django class based views

I've been struggling with class views all day after starting on them yesterday. My issue is constantly getting 'str' object has no attribute 'visible_fields', so the 'form' item below is not really a form:
template-
<form action="" method="post">
{% csrf_token %}
{{form|bootstrap}}
<input type="submit" name="submit" value="Add new article"/>
</form>
view-
class ArticleCreateView(CreateView):
model = Article
template_name = 'index/add_article.html'
form_class = ArticleForm
def post(self, request, *args, **kwargs):
article_form = self.get_form()
if article_form.is_valid():
article = article_form.save(commit=False)
title = article_form.cleaned_data['title']
url = article_form.cleaned_data['url']
title = process_title(url)
article.title = title
article.save()
return redirect("index:article_list")
else:
form = ArticleForm()
print type(form)
print dir(self)
return render(request, 'index/add_article.html')
The worst part is printing type(form) shows it is <class 'index.forms.ArticleForm'>. I'm trying to just have it redirect to the list view if the form saved, and replay the form with the error (You already have an article with that URL) if the form is bad. I heard class views are easier to work with and huge projects I've read through use them, but they really seem worse than the old views. I assume that's because I'm not using them well
Every example I've seen has a template getting a "form" somehow, like
class RangeCreateView(CreateView):
model = Range
template_name = 'dashboard/ranges/range_form.html'
form_class = RangeForm
def get_success_url(self):
if 'action' in self.request.POST:
return reverse('dashboard:range-products',
kwargs={'pk': self.object.id})
else:
msg = render_to_string(
'dashboard/ranges/messages/range_saved.html',
{'range': self.object})
messages.success(self.request, msg, extra_tags='safe noicon')
return reverse('dashboard:range-list')
def get_context_data(self, **kwargs):
ctx = super(RangeCreateView, self).get_context_data(**kwargs)
ctx['title'] = _("Create range")
return ctx
then like magic in range_form.html:
{% include "dashboard/partials/form_fields.html" with form=form %}
My issue here is I need to process the title of the form, with
def process_title(url):
def _search_for_title(url):
try:
r = requests.get(url)
content = r.text
t = html.document_fromstring(content)
return t.find(".//title").text
except IOError:
return None
title = _search_for_title(url)
return title or 'None'
This kind of ruins the purpose of a class based view. It seems I should be processing the title by overriding 'clean' in the form itself?
Otherwise, how can I make this view pass a form object, render it in the template, and just re-render the template if the form didn't pass?
And how can I access the form in the template?
Thank you
You do it by following the example view, rather than overriding post instead as you are doing.
In your case, simply displaying and processing a form is the default behaviour of a CreateView. So there is no need to override any methods at all. Your view should just be:
class ArticleCreateView(CreateView):
model = Article
template_name = 'index/add_article.html'
form_class = ArticleForm

Save post data from a form with listview

i have a listview of form:
class MatchsView(ListView):
model = Match2x1
template_name = 'matchs.html'
and the template render this:
{% for match in object_list %}
<form action="/apostar/" method="post">{% csrf_token %}
<p><input type="radio" name="{{match.id}}" value="{{match.team_a}}">{{match.team_a}}</input></p>
<input type="submit" value="Apostar"></input>
</form>
{% endfor %}
as you can see each form has two fields, i need to save in DB the values that the user choose, with FormView its easy, but since this time is a ListView im a little bit lose to save in DB from a form, i know that i have to create a view that handles the form, but really i dont know how to create the view that handles the post data of each form. For example lets says that i need to save the post data in a model called FormsMatchs, how can i do it?
i was trying with this:
class FormView(FormView):
form_class = FormMatch
success_url = '/'
template_name = 'matchs.html'
def post(self, request, *args, **kwargs):
hola = Country.objects.create(name=request.POST)
but is saving this:
<QueryDict: {u'csrfmiddlewaretoken': [u'tCIQuGlSXKJL0R5eo9R5w09ldeBt7zNW'], u'5': [u'River']}>
Your best bet if you want to have a simple list of a given model but also accept a Form is to use a FormView, and override get_context_data(self, **kwargs) to pass a queryset into the context, like so:
def get_context_data(self, **kwargs):
context = super(MatchsView, self).get_context_data()
context['object_list'] = Match2x1.objects.all()
return context
However, you can also use a FormMixin with a ListView, see an example here.

Django: Can class-based views accept two forms at a time?

If I have two forms:
class ContactForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
class SocialForm(forms.Form):
name = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
and wanted to use a class based view, and send both forms to the template, is that even possible?
class TestView(FormView):
template_name = 'contact.html'
form_class = ContactForm
It seems the FormView can only accept one form at a time.
In function based view though I can easily send two forms to my template and retrieve the content of both within the request.POST back.
variables = {'contact_form':contact_form, 'social_form':social_form }
return render(request, 'discussion.html', variables)
Is this a limitation of using class based view (generic views)?
Many Thanks
Here's a scaleable solution. My starting point was this gist,
https://gist.github.com/michelts/1029336
i've enhanced that solution so that multiple forms can be displayed, but either all or an individual can be submitted
https://gist.github.com/jamesbrobb/748c47f46b9bd224b07f
and this is an example usage
class SignupLoginView(MultiFormsView):
template_name = 'public/my_login_signup_template.html'
form_classes = {'login': LoginForm,
'signup': SignupForm}
success_url = 'my/success/url'
def get_login_initial(self):
return {'email':'dave#dave.com'}
def get_signup_initial(self):
return {'email':'dave#dave.com'}
def get_context_data(self, **kwargs):
context = super(SignupLoginView, self).get_context_data(**kwargs)
context.update({"some_context_value": 'blah blah blah',
"some_other_context_value": 'blah'})
return context
def login_form_valid(self, form):
return form.login(self.request, redirect_url=self.get_success_url())
def signup_form_valid(self, form):
user = form.save(self.request)
return form.signup(self.request, user, self.get_success_url())
and the template looks like this
<form class="login" method="POST" action="{% url 'my_view' %}">
{% csrf_token %}
{{ forms.login.as_p }}
<button name='action' value='login' type="submit">Sign in</button>
</form>
<form class="signup" method="POST" action="{% url 'my_view' %}">
{% csrf_token %}
{{ forms.signup.as_p }}
<button name='action' value='signup' type="submit">Sign up</button>
</form>
An important thing to note on the template are the submit buttons. They have to have their 'name' attribute set to 'action' and their 'value' attribute must match the name given to the form in the 'form_classes' dict. This is used to determine which individual form has been submitted.
By default, class-based views only support a single form per view. But there are other ways to accomplish what you need. But again, this cannot handle both forms at the same time. This will also work with most of the class-based views as well as regular forms.
views.py
class MyClassView(UpdateView):
template_name = 'page.html'
form_class = myform1
second_form_class = myform2
success_url = '/'
def get_context_data(self, **kwargs):
context = super(MyClassView, self).get_context_data(**kwargs)
if 'form' not in context:
context['form'] = self.form_class(request=self.request)
if 'form2' not in context:
context['form2'] = self.second_form_class(request=self.request)
return context
def get_object(self):
return get_object_or_404(Model, pk=self.request.session['value_here'])
def form_invalid(self, **kwargs):
return self.render_to_response(self.get_context_data(**kwargs))
def post(self, request, *args, **kwargs):
self.object = self.get_object()
if 'form' in request.POST:
form_class = self.get_form_class()
form_name = 'form'
else:
form_class = self.second_form_class
form_name = 'form2'
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(**{form_name: form})
template
<form method="post">
{% csrf_token %}
.........
<input type="submit" name="form" value="Submit" />
</form>
<form method="post">
{% csrf_token %}
.........
<input type="submit" name="form2" value="Submit" />
</form>
Its is possible for one class-based view to accept two forms at a time.
view.py
class TestView(FormView):
template_name = 'contact.html'
def get(self, request, *args, **kwargs):
contact_form = ContactForm()
contact_form.prefix = 'contact_form'
social_form = SocialForm()
social_form.prefix = 'social_form'
# Use RequestContext instead of render_to_response from 3.0
return self.render_to_response(self.get_context_data({'contact_form': contact_form, 'social_form': social_form}))
def post(self, request, *args, **kwargs):
contact_form = ContactForm(self.request.POST, prefix='contact_form')
social_form = SocialForm(self.request.POST, prefix='social_form ')
if contact_form.is_valid() and social_form.is_valid():
### do something
return HttpResponseRedirect(>>> redirect url <<<)
else:
return self.form_invalid(contact_form,social_form , **kwargs)
def form_invalid(self, contact_form, social_form, **kwargs):
contact_form.prefix='contact_form'
social_form.prefix='social_form'
return self.render_to_response(self.get_context_data({'contact_form': contact_form, 'social_form': social_form}))
forms.py
from django import forms
from models import Social, Contact
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Button, Layout, Field, Div
from crispy_forms.bootstrap import (FormActions)
class ContactForm(forms.ModelForm):
class Meta:
model = Contact
helper = FormHelper()
helper.form_tag = False
class SocialForm(forms.Form):
class Meta:
model = Social
helper = FormHelper()
helper.form_tag = False
HTML
Take one outer form class and set action as TestView Url
{% load crispy_forms_tags %}
<form action="/testview/" method="post">
<!----- render your forms here -->
{% crispy contact_form %}
{% crispy social_form%}
<input type='submit' value="Save" />
</form>
Good Luck
I have used a following generic view based on TemplateView:
def merge_dicts(x, y):
"""
Given two dicts, merge them into a new dict as a shallow copy.
"""
z = x.copy()
z.update(y)
return z
class MultipleFormView(TemplateView):
"""
View mixin that handles multiple forms / formsets.
After the successful data is inserted ``self.process_forms`` is called.
"""
form_classes = {}
def get_context_data(self, **kwargs):
context = super(MultipleFormView, self).get_context_data(**kwargs)
forms_initialized = {name: form(prefix=name)
for name, form in self.form_classes.items()}
return merge_dicts(context, forms_initialized)
def post(self, request):
forms_initialized = {
name: form(prefix=name, data=request.POST)
for name, form in self.form_classes.items()}
valid = all([form_class.is_valid()
for form_class in forms_initialized.values()])
if valid:
return self.process_forms(forms_initialized)
else:
context = merge_dicts(self.get_context_data(), forms_initialized)
return self.render_to_response(context)
def process_forms(self, form_instances):
raise NotImplemented
This has the advantage that it is reusable and all the validation is done on the forms themselves.
It is then used as follows:
class AddSource(MultipleFormView):
"""
Custom view for processing source form and seed formset
"""
template_name = 'add_source.html'
form_classes = {
'source_form': forms.SourceForm,
'seed_formset': forms.SeedFormset,
}
def process_forms(self, form_instances):
pass # saving forms etc
It is not a limitation of class-based views. Generic FormView just is not designed to accept two forms (well, it's generic). You can subclass it or write your own class-based view to accept two forms.
Use django-superform
This is a pretty neat way to thread a composed form as a single object to outside callers, such as the Django class based views.
from django_superform import FormField, SuperForm
class MyClassForm(SuperForm):
form1 = FormField(FormClass1)
form2 = FormField(FormClass2)
In the view, you can use form_class = MyClassForm
In the form __init__() method, you can access the forms using: self.forms['form1']
There is also a SuperModelForm and ModelFormField for model-forms.
In the template, you can access the form fields using: {{ form.form1.field }}. I would recommend aliasing the form using {% with form1=form.form1 %} to avoid rereading/reconstructing the form all the time.
Resembles #james answer (I had a similar starting point), but it doesn't need to receive a form name via POST data. Instead, it uses autogenerated prefixes to determine which form(s) received POST data, assign the data, validate these forms, and finally send them to the appropriate form_valid method. If there is only 1 bound form it sends that single form, else it sends a {"name": bound_form_instance} dictionary.
It is compatible with forms.Form or other "form behaving" classes that can be assigned a prefix (ex. django formsets), but haven't made a ModelForm variant yet, tho you could use a model form with this View (see edit below). It can handle forms in different tags, multiple forms in one tag, or a combination of both.
The code is hosted on github (https://github.com/AlexECX/django_MultiFormView). There are some usage guidelines and a little demo covering some use cases. The goal was to have a class that feels as close as possible like the FormView.
Here is an example with a simple use case:
views.py
class MultipleFormsDemoView(MultiFormView):
template_name = "app_name/demo.html"
initials = {
"contactform": {"message": "some initial data"}
}
form_classes = [
ContactForm,
("better_name", SubscriptionForm),
]
# The order is important! and you need to provide an
# url for every form_class.
success_urls = [
reverse_lazy("app_name:contact_view"),
reverse_lazy("app_name:subcribe_view"),
]
# Or, if it is the same url:
#success_url = reverse_lazy("app_name:some_view")
def get_contactform_initial(self, form_name):
initial = super().get_initial(form_name)
# Some logic here? I just wanted to show it could be done,
# initial data is assigned automatically from self.initials anyway
return initial
def contactform_form_valid(self, form):
title = form.cleaned_data.get('title')
print(title)
return super().form_valid(form)
def better_name_form_valid(self, form):
email = form.cleaned_data.get('email')
print(email)
if "Somebody once told me the world" is "gonna roll me":
return super().form_valid(form)
else:
return HttpResponse("Somebody once told me the world is gonna roll me")
template.html
{% extends "base.html" %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ forms.better_name }}
<input type="submit" value="Subscribe">
</form>
<form method="post">
{% csrf_token %}
{{ forms.contactform }}
<input type="submit" value="Send">
</form>
{% endblock content %}
EDIT - about ModelForms
Welp, after looking into ModelFormView I realised it wouldn't be that easy to create a MultiModelFormView, I would probably need to rewrite SingleObjectMixin as well. In the mean time, you can use a ModelForm as long as you add an 'instance' keyword argument with a model instance.
def get_bookform_form_kwargs(self, form_name):
kwargs = super().get_form_kwargs(form_name)
kwargs['instance'] = Book.objects.get(title="I'm Batman")
return kwargs