I am pretty new to Python and Django, but what I am trying to do is to use a Crispy Form in one of my template that is loaded with a ListView type of view. I usually load all of my other templates from a UpdateView which provide everything Crispy need already. I can't change the type of view here so I have to stick with the ListView but when I try to load the form, Crispy can't find it. I don't know how to provide the form to the template manually.
Here is what I have so far:
My template:
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load i18n %}
{% load tz %}
{% block content %}
{% crispy form %}
<!-- template content -->
{% endblock %}
My form.py
class UserBirthdayForm(forms.ModelForm):
birth_day = forms.IntegerField(required=True, localize=True, label=_('*My birth day'))
birth_month = forms.IntegerField(required=True, localize=True, label=_('*My birth month'))
birth_year = forms.IntegerField(required=True, localize=True, label=_('*My birth year'))
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Div(
Div('birth_day', css_class='col-sm-4'),
Div('birth_month', css_class='col-sm-4'),
Div('birth_year', css_class='col-sm-4'),
css_class='row'
),
Submit('save', _('update'), css_class='pull-right'),
)
class Meta():
model = User
fields = ("birth_day", "birth_month", "birth_year")
My view.py:
class MissionList(LoginRequiredMixin, ListView):
form_class = UserBirthdayForm
def get_queryset(self):
#some other stuff
return queryset
def get_context_data(self, **kwargs):
#some other stuff
return context
Here is the error I get from Django when trying to access the page:
VariableDoesNotExist: Failed lookup for key [form] in u"[some other stuff]"
In get_context_data create an instance of your form and add it to the context.
def get_context_data(self, **kwargs):
#some other stuff — where you create `context`
context["form"] = UserBirthdayForm()
return context
Related
I have created a custom mixin GetVerboseNameMixin in order to get the verbose name of model fields, and then display these in my html template using a DetailView. However, whenever I try and render the list of verbose names nothing is returned, and I cannot work out why.
Mixin.py:
class GetVerboseNameMixin:
def get_verbose_name(model, fields=[]):
verbose_names = []
for field in fields:
verbose_names.append(str(model._meta.get_field(field)))
return verbose_names
View:
class ShowProfileView(GetVerboseNameMixin, DetailView):
model = Profile
template_name = 'profiles/user_profile.html'
verbose_model_fields = GetVerboseNameMixin.get_verbose_name(model=Profile, fields=['first_name', 'surname', 'date_of_birth', 'phone_number', 'bio', 'gender', 'emergency_contact_name', 'emergency_contact_number'])
def get_context_data(self, *args, **kwargs):
context = super(ShowProfileView, self).get_context_data(*args, **kwargs)
user_profile = get_object_or_404(Profile, id=self.kwargs['pk'])
context["user_profile"] = user_profile
return context
def get_object(self, *args, **kwargs):
obj = Profile.objects.filter(id=self.kwargs['pk']).values('first_name', 'surname', 'date_of_birth', 'phone_number', 'bio', 'gender', 'emergency_contact_name', 'emergency_contact_number') # list of dictionaries
object = obj[0]
return object
Html template:
{% extends "base.html" %}
{% block content %}
<h1>Profile</h1>
<br/><br/>
{% csrf_token %}
<ul>
{% for v in object.values %}
{% for field_name in verbose_model_fields %}
<p>{{field_name}}: {{ v }}</p>
{% endfor %}
{% endfor %}
</ul>
<a href='{% url "profiles:edit_profile" pk=user.profile.id %}'>Edit Profile</a>
{% endblock %}
Even if I just render:
{{ verbose_model_fields }}
In my html file nothing is being displayed. This leads me to think maybe the problem is in my mixin, or perhaps the function is not being called properly?
I do not get how verbose_model_fields is getting passed to the template from the view, and also, I did not find any reference in DetailView documentation. I assume you want to have this custom implementation, if so, then you need to pass along this parameter via context:
class ShowProfileView(GetVerboseNameMixin, DetailView):
...
def get_context_data(self, *args, **kwargs):
context = super(ShowProfileView, self).get_context_data(*args, **kwargs)
user_profile = get_object_or_404(Profile, id=self.kwargs['pk'])
context["user_profile"] = user_profile # redundant implementation, you can get this value by `object` context variable in template
context["verbose_model_fields"] = self.verbose_model_fields # or just call self.get_verbose_name(...) method
return context
Also in this solution I have marked redundant implementation that you do not need to re-implement how to get object, because it is already taken care by DetailView.
Hello how can I pass form into template from class based view? In HTML everything inherits and I can render elements inside block content but I can not render form. This is my code. :
views.py:
class Signup(TemplateView):
model = Profile
template_name = 'home/sign-up.html'
form_class = UserCreationForm()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = UserCreationForm
HTML:
{% extends "home/todo.html" %}
{% block content %}
<form method="POST">
{{form}}
</form>
{% endblock content %}
Give this a try
context['form'] = self.form_class
should work
But for User creation, you may better use CreateView instead of TemplateView
from django.views.generic import CreateView
class Signup(CreateView):
template_name = 'home/sign-up.html'
form_class = UserCreationForm()
So I have two forms.ModelForm for my two models
First:
class TranslatorChoice(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user_id = kwargs.pop('user_id',None)
super(TranslatorChoice, self).__init__(*args, **kwargs)
self.fields['owner'].queryset = Translator.objects.all().filter(owner_id = self.user_id)
owner = forms.ModelChoiceField(queryset = None)
class Meta:
model = Translator
fields = ('owner',)
Second:
class ProfileChoice(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user_id = kwargs.pop('user_id',None)
super(ProfileChoice, self).__init__(*args, **kwargs)
self.fields['login'].queryset = Profile.objects.all().filter(created_by_id = self.user_id)
login = forms.ModelChoiceField(queryset= None, label='Profile')
class Meta:
model = Profile
fields = ('login',)
I've tried writing a view for them but it doesn't work, seems like it just won't save because whenever I hit submit button it just refreshes the page and cleans the fields without redirecting me to needed URL. The model instances in my DB aren't updated either.
Here's the view:
def link_profile(request):
context = {
'form': ProfileChoice(user_id=request.user.id),
'form2': TranslatorChoice(user_id=request.user.id)
}
if request.method == 'POST':
form = ProfileChoice(request.POST)
form2 = TranslatorChoice(request.POST)
if form.is_valid():
login = form.cleaned_data.get('login')
translator = form.cleaned_data.get('owner')
link = Profile.objects.get(login=login)
link.owner = login
link.save(['owner'])
form.save()
form2.save()
return redirect('dashboard')
return render(request, 'registration/link.html', context)
I know also something is wrong is because I am using to many save functions. I just don't have any experience in creating views like that...
Sharing my template:
{% extends 'base.html' %}
{% block content %}
<h2>Add profile</h2>
<form method="post">
{% csrf_token %}
<table>
{{ form.as_table }} {{ form2.as_table }}
</table>
<button type="submit">Link</button>
</form>
{% endblock %}`
And my urls.py part with the view:
url(r'^link/', views.link_profile),
You didn't share your urls.py or the form in your template so it's not clear if the view is being executed, or how you're passing your forms. But, here's something that might work if you're not doing it already.
{{ form.as_table }}
{{ form2.as_table }}
FYI: there are some indentation issues with your code but I'm assuming that that's just something that happened when you pasted it here.
So below is the views.py and I'm trying to load a view with modelform. And within the modelform I need to load modelchoicefield depending on the current user logged in and tried the below solution(check forms.py.) When I run it, i get
Attribute Error :object has no attribute 'get'
Help is highly appreciated, there's nothing in stackoverflow.
views.py:
class HomeView(View):
def get(self, request, *args, **kwargs):
form=PreDataForm(request.user)
return render(request, 'mainlist.html',
{ "form":form,
})
models.py:
class PreData(models.Model):
journalname = models.CharField(max_length=400, blank=False, null=True, default='')
forms.py:
class PreDataForm(forms.ModelForm):
journalname = forms.ModelChoiceField(required=True)
class Meta:
model=PreData
fields=['journalname']
def __init__(self,user, *args, **kwargs):
super(PreDataForm, self).__init__(user, *args, **kwargs)
self.fields["journalname"].queryset = Journals.objects.filter(journalusername=user)
html file:
{% extends 'home-base.html' %}
{% load crispy_forms_tags %}
{% block title %}
Welcome to Metrics - JSM
{% endblock %}
{% block content %}
<div class="col-md-9 col-centered" >
<div class="backeffect" >
{% if data %}
{% else %}
<b>Seems you are first time around here, Why not <b>{% include 'modal_first_stage.html' %}</b> to get started? :)</b>
{% endif %}
{% endblock %}
ModelForm does not take user. You should remove it from your super() call.
def __init__(self, user, *args, **kwargs):
super(PreDataForm, self).__init__(*args, **kwargs)
self.fields["journalname"].queryset = Journals.objects.filter(journalusername=user)
A slightly more re-usable version, which will work with the generic create/update views, is:
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(PreDataForm, self).__init__(*args, **kwargs)
self.fields["journalname"].queryset = Journals.objects.filter(journalusername=user)
I have a model Book and a model Review (with a ForeignKey to Book). I wanted to create a view where you have all the data related to a book (DetailView), and add the functionality of showing and creating reviews. I came up with this code, but don't know whether it is a good practice, maybe I should go for something different:
class BookDetailView(CreateView):
template_name = 'books/book_detail.html'
form_class = ReviewForm
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
slug = self.kwargs.get('slug')
obj = Book.objects.get(slug__iexact=slug)
if get_language() == 'es':
context['reviews'] = obj.review_set.all().filter(language__iexact='es')
else:
context['reviews'] = obj.review_set.all().filter(language__iexact='en')
if len(context['reviews']) == 0:
context['is_empty'] = True
context['object'] = obj
return context
def form_valid(self, form):
obj = form.save(commit=False)
return super().form_valid(form)
And the template:
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<h1>{{object.title}}</h1>
<h2>{% trans "Reviews section" %}</h2>
{% for review in reviews %}
<b>{{review.title}}
{% endfor %}
<h2>Add a review!</h2>
{% include "form.html" with form=form %}
{% endblock content %}
And finally the url: url(r'^(?P[\w-]+)/$', ...)
What do you think?
Thanks for your time!
I did something similar once. But used the DetailView and just added the ReviewForm to the context and added a method to handle post data. Something like this:
class BookDetailView(DetailView):
model = Book
def get_context_data(self, *args, **kwargs):
ctx = super().get_context_data(*args, **kwargs)
language = get_language()
ctx.update({
'reviews': ctx['book'].review_set.all().filter(language__iexact=language),
'form': ReviewForm()
})
return ctx
def post(self, *args, **kwargs):
self.object = self.get_object(self.get_queryset())
form = ReviewForm(self.request.POST)
if form.is_valid():
form.instance.book = self.object
form.save()
return HttpResponseRedirect(self.object.get_absolute_url())
else:
ctx = self.get_context_data(**kwargs)
ctx.update({'form': form})
return self.render_to_response(ctx)
I guess this takes a little more code for handling the form, but the bonus is you can set the related book (the user can't fiddle with that form data).
Note that you don't have to specify template_name because it is automagical correct.
I know this post is a little bit old but it helps me a lot with my issue. Thank you #allcaps
First of all, I would like to mention that the "ReviewForm" should inherit from forms.ModelForm and than we will have a access to "instance" and "save()"
More about ModelForm you can read in django docs Django ModelForms