I am developping in Django.
But while I am trying to run my code, I got an ImproperlyConfigured error...
Error:
ImproperlyConfigured at /acl/
PermissionGroupView is missing a QuerySet. Define PermissionGroupView.model, PermissionGroupView.queryset, or override PermissionGroupView.get_queryset().
Views.py:
class PermissionGroupView(LoginRequiredMixin, generic.CreateView):
template_name = 'acl/acl-dashboard.html'
success_url = '/acl/'
def get_context_data(self, **kwargs):
context = super(PermissionGroupView, self).get_context_data(**kwargs)
if self.request.method == 'POST':
context['groups'] = GroupForm(self.request.POST)
if context['groups'].is_valid():
context['groups'].save()
return HttpResponseRedirect(self.get_success_url())
else:
context['groups'] = GroupForm()
return context
Forms.py:
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = '__all__'
acl-dashboard.html:
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h4 class="card-title">Enter New Group</h4>
<div class="row">
<div class="col-md-8">
<form class="forms-sample" action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ groups | crispy}}
<button class="btn btn-success mr-2" type="submit">Save</button>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
If someone knows how to help me!
As the error said, you need to define either model or queryset. So change your view like this:
class PermissionGroupView(LoginRequiredMixin, generic.CreateView):
model = Group # <-- Here, we defined the model.
template_name = 'acl/acl-dashboard.html'
success_url = '/acl/'
For more details, please see CreateView documentation.
OR, you can add form_class to your view, which will also attach the model name to model attribute of the view:
class PermissionGroupView(LoginRequiredMixin, generic.CreateView):
form_class = GroupForm # <-- Here, we defined the form.
template_name = 'acl/acl-dashboard.html'
success_url = '/acl/'
Related
I'm using django parler to translate my models. now i'm creating a custom admin Panel and i have a view for create and update of Contents. I'm using a class based view inherit from "View" for both create and update views so i can't use the TranslatableCreateView and TranslatableUpdateView. I saw in the codes of Django parler that using TranslatableModelFormMixin you can add translation support to the class-based views. I used this mixin but still i don't have access to the language tabs.
Here is Views.py:
class ContentCreateUpdateView(TranslatableModelFormMixin, TemplateResponseMixin, View):
module = None
model = None
obj = None
template_name = 'content-form.html'
def get_model(self, model_name):
if model_name in ['text', 'video', 'image', 'file']:
return apps.get_model(app_label='courses', model_name=model_name)
return None
def get_form(self, model, *args, **kwargs):
Form = modelform_factory(model, exclude=['owner',
'order',
'created',
'updated'])
return Form(*args, **kwargs)
def dispatch(self, request, module_id, model_name, id=None):
self.module = get_object_or_404(Module, id=module_id, course__owner=request.user)
self.model = self.get_model(model_name)
if id:
self.obj = get_object_or_404(self.model,
id=id,
owner=request.user)
return super().dispatch(request, module_id, model_name, id)
def get(self, request, module_id, model_name, id=None):
form = self.get_form(self.model, instance=self.obj,)
return self.render_to_response({'form': form, 'object': self.obj})
def post(self, request, module_id, model_name, id=None):
form = self.get_form(self.model, instance=self.obj, data=request.POST, files=request.FILES)
if form.is_valid():
obj = form.save(commit=False)
obj.owner = request.user
obj.save()
if not id:
# new content
Content.objects.create(module=self.module, item=obj)
return redirect('module_content_list', self.module.id)
return self.render_to_response({'form': form, 'object': self.obj})
Template:
{% extends '_base.html' %}
{% load crispy_forms_tags %}
{% load crispy_forms_filters %}
{% block content %}
<div class="col-md-12">
<!-- Horizontal Form -->
<div class="card card-primary">
<div class="card-header ">
{% if object %}
<h3 class="card-title mb-0 float-left"> Edit Content "{{ object.title }}"</h3>
{% else %}
<h3 class="card-title mb-0 float-left"> Add New Content</h3>
{% endif %}
</div>
<!-- /.card-header -->
<!-- form start -->
<div class="card-body">
<form method="post" enctype="multipart/form-data">
<div class="row">
<div class="col-12">
{% if language_tabs %}
<ul class="nav nav-tabs">
{% for url,name,code,status in language_tabs %}
{% if status == 'current' %}
<input type="hidden" class="language_button selected" name="{{ code }}"/>
<li class="nav-item">
<a class="current nav-link active"
aria-selected="true">{{ name }}</a>
</li>
{% else %}
<li class="nav-item">
<a class="{{ status }} nav-link"
href="{{ url }}"
aria-selected="false">{{ name }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</div>
<div class="col-6">
{{ form }}
</div>
</div>
{% csrf_token %}
<div class="col-6">
<button class="btn btn-success">Save Content</button>
</div>
</form>
</div>
</div>
</div>
<!-- /.card -->
{% endblock %}
Here is the source code of django parler:
class TranslatableModelFormMixin(LanguageChoiceMixin):
"""
Mixin to add translation support to class based views.
For example, adding translation support to django-oscar::
from oscar.apps.dashboard.catalogue import views as oscar_views
from parler.views import TranslatableModelFormMixin
class ProductCreateUpdateView(TranslatableModelFormMixin, oscar_views.ProductCreateUpdateView):
pass
"""
def get_form_class(self):
"""
Return a ``TranslatableModelForm`` by default if no form_class is set.
"""
super_method = super().get_form_class
# no "__func__" on the class level function in python 3
default_method = getattr(
ModelFormMixin.get_form_class, "__func__", ModelFormMixin.get_form_class
)
if not (super_method.__func__ is default_method):
# Don't get in your way, if you've overwritten stuff.
return super_method()
else:
# Same logic as ModelFormMixin.get_form_class, but using the right form base class.
if self.form_class:
return self.form_class
else:
model = _get_view_model(self)
if self.fields:
fields = self.fields
return modelform_factory(model, form=TranslatableModelForm, fields=fields)
else:
return modelform_factory(model, form=TranslatableModelForm)
def get_form_kwargs(self):
"""
Pass the current language to the form.
"""
kwargs = super().get_form_kwargs()
# The TranslatableAdmin can set form.language_code, because the modeladmin always creates a fresh subclass.
# If that would be done here, the original globally defined form class would be updated.
kwargs["_current_language"] = self.get_form_language()
return kwargs
# Backwards compatibility
# Make sure overriding get_current_language() affects get_form_language() too.
def get_form_language(self):
return self.get_current_language()
The tabs should look like this:
But now it looks like this:
If someone have a similiar exprience, feel free to write your opinion
In the settings.py file find PARLER_LANGUAGES settings and change None to SITE_ID.
PARLER_LANGUAGES = {
1: (
{'code': 'en', }, # English
{'code': 'ru', }, # Russian
),
'default': {
'fallbacks': ['en'],
'hide_untranslated': False,
}
}
I created a form using Django's UpdateView class, however, when the form loads it seems like the text boxes and text areas are not styled (looks like form.as_p style). Here is an example of exactly what I did.
Views.py
class UpdatePostView(UpdateView):
template_name = 'Post/UpdatePost.html'
model = Post
fields = ['Title', 'Body']
success_url = reverse_lazy('BlogApp:main')
def form_valid(self, form):
form.instance.Title = form.cleaned_data['Title']
form.instance.Body = form.cleaned_data['Body']
form.instance.save()
return super().form_valid(form)
Here is how I loaded the form in UpdatePost.html:
<form id="UpdatePostForm" method="POST">
{% csrf_token %}
<div class="form-group">
<label for="PostTitle">{{form.Title.label}}</label>
{{form.Title}}
</div>
<div class="form-group">
<label for="PostBody">{{form.Body.label}}</label>
{{form.Body}}
</div>
<input class="btn btn-primary" type="submit" for="UpdatePostForm" value="Update">
</div>
</form>
If you use Bootstrap, you also can use django-crispy-forms (version for Bootstrap 4 https://github.com/django-crispy-forms/django-crispy-forms ,version for Bootstrap 5 - https://github.com/django-crispy-forms/crispy-bootstrap5). It is helpful to live DRY (Don't repeat yourself).
And then it will be something like(I use crispy-forms for Bootstrap 5):
pip install crispy-bootstrap5
INSTALLED_APPS = (
...
"crispy_forms",
"crispy_bootstrap5",
...
)
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
CRISPY_TEMPLATE_PACK = "bootstrap5"
class UpdatePostView(UpdateView):
template_name = 'Post/UpdatePost.html'
model = Post
fields = ['Title', 'Body']
success_url = reverse_lazy('BlogApp:main')
def form_valid(self, form):
form.instance.Title = form.cleaned_data['Title']
form.instance.Body = form.cleaned_data['Body']
form.instance.save()
return super().form_valid(form)
template
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<h1 class="text-center">Update Post</h1>
<br />
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form|crispy }}
<button class="btn btn-outline-primary">
Update
</button>
</form>
{% endblock content %}
Because by default the form.body and form.title render a html input, you can override the class attribut from your UpdateView like that :
def get_form(self, *args, **kwargs):
form = super(UpdatePostView, self).get_form(*args, **kwargs)
form.fields["Title"].widget.attrs["class"] = "form-group"
form.fields["Body"].widget.attrs["class"] = "form-group"
return form
I'm fairly new to this, but what I'm trying to do is get my form to display (injected as part of a template) in another view. In developer tools I see the HTML for my included page (polls/_poll_form.html), but not the form. I would appreciate it if someone could point me in the right direction.
models.py
class Poll(models.Model):
poll_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, unique=True)
topic = models.ForeignKey(
Topic,
related_name = 'polls',
on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
last_updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse(
'polls:single',
kwargs={'pk':self.pk}
)
class Meta:
db_table = 'polls'
ordering = ['last_updated_at']
views.py
class CreatePoll(LoginRequiredMixin, generic.CreateView):
template_name = 'polls/_poll_form.html'
model = Poll
_poll_form.html (injected template)
<div class="container poll-form-header">
<p class="text-center">Get Started</p>
</div>
<form class="create-poll-form" action="{% url 'topics:single' pk=topic.topic_id %}" method="POST">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" class="btn btn-dark float-right">
</form>
topic_detail.html
{% extends "topics/topic_base.html" %}
{%block topics_content %}
<div class="col-md-12 list-group polls-list">
<div class="container new-poll-button">
<a class = "btn btn-dark float-right mt-2" data-toggle="collapse" href="#poll-form" role="button" aria-expanded="false">Create Poll</a>
</div>
<div class="collapse mt-2 new-poll-form" id="poll-form">
<div class="card card-body">
{% include "polls/_poll_form.html" %}
</div>
</div>
{% if topic.polls.count == 0 %}
<br>
<div class="container no-polls-message">
<p>There are no polls for this topic. Create the first!</p>
</div>
{% else %}
{% for poll in topic.polls.all %}
{% include "polls/_poll.html" %}
{% endfor %}
{% endif %}
</div>
{% endblock %}
This appears to be a fairly common confusion, but I don't really understand how it arises.
Just including a template in another one doesn't mean that a view which mentions that template is executed. Views render templates, templates don't call views. Views are only called by the user requesting a URL which is handled by that view. In your case, the URL is pointing to a completely different URL, and the one that creates the form is never called.
You need to include the form in the context of the view that your URL is actually calling. Either do this explicitly in the get_context_data method, or - if the form needs to appear on multiple pages - create a custom template tag that inserts a rendered template, including the form.
Make a forms.py in your app.
Write something like this:
from .models import Poll
class PollForm(forms.ModelForm):
class Meta:
model = Poll
fields = ('name', 'topic',)
And then import PollForm in views.py and pass it to template
from polls.forms import PollForm
class CreatePoll(LoginRequiredMixin, generic.CreateView):
template_name = 'polls/_poll_form.html'
model = Poll
form_class = PollForm
I have a page which displays posts of all user and user can only delete his posts.
Heres the code:
class PostDelete(generic.DeleteView):
model = Post
template_name = 'dashboard/post_delete.html'
success_url = reverse_lazy('dashboard:posts')
post_delete.html:
{% extends 'dashboard/sidebar.html' %}
{% block title %}Confirmation{% endblock %}
{% block mainpage %}
<div id="page-wrapper" align="center">
<div id="page-inner">
<h1>New post</h1>
<form method="post">
{% csrf_token %}
Are you sure you want to delete?
<br>
<button class="btn btn-danger">Yes</button>
No
</form>
</div>
</div>
{% endblock %}
Urls.py:
path('delete/<int:pk>',views.PostDelete.as_view(),name='delete'),
How do I add user authentication code?
If it were a function I would have used " if request.user.is_authenticated "
But I don't know how to achieve this thing in a class. If you need an excerpt of another code then comment. Thanks!
Try to use UserPassesTestMixin:
from django.contrib.auth.mixins import UserPassesTestMixin
class PostDelete(UserPassesTestMixin, generic.DeleteView):
model = Post
template_name = 'dashboard/post_delete.html'
success_url = reverse_lazy('dashboard:posts')
raise_exception = True
def test_func(self):
self.object = self.get_object()
return self.object.user == self.request.user
I am building a view that will let me update multiple fields on multiple objects at the same time. I'm doing this using ModelFormSet & modelformset_factory.
The template will be a table of forms with the object name to the left of the fields (see image below).
I found this example, but I am stuck on how to implement the class based view & template.
My Formset
class BaseFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseFormSet, self).__init__(*args, **kwargs)
self.queryset = Reference.objects.filter(
start__isnull=True)
ReferenceFormSet = modelformset_factory(
Reference,
fields=('start', 'end'),
formset=BaseFormSet,
extra=0)
My View
class ReferenceFormSetView(LoginRequiredMixin, SuperuserRequiredMixin, FormView):
model = Reference
form_class = ReferenceFormSet
template_name = "references/references_form.html"
def form_valid(self, form):
for sub_form in form:
if sub_form.has_changed():
sub_form.save()
return super(ReferenceFormSetView, self).form_valid(form)
My Template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<h1>{{ headline }}</h1>
<div class="row">
<form action="" method="post">
{% crispy form %}
<div class="">
<input type="submit" value="Submit" />
</div>
</form>
</div>
</div>
{% endblock content %}
Questions
The view seems odd with the Formset in the form_class. Is there a better way to handle this?
How can I access the instance name to display in the form?
I found a solution using a package called django-extra-views.
There is a class called ModelFormSetView which does exactly what I wanted. Here is my implementation (simplified) for others to use -
My View
class ReferenceFormSetView(ModelFormSetView):
model = Reference
template_name = "references/references_form.html"
fields = ['start', 'end']
extra = 0
def get_queryset(self):
return self.model.objects.all()
def get_success_url(self):
return reverse('references:formset')
def formset_valid(self, formset):
"""
If the formset is valid redirect to the supplied URL
"""
messages.success(self.request, "Updated")
return HttpResponseRedirect(self.get_success_url())
def formset_invalid(self, formset):
"""
If the formset is invalid, re-render the context data with the
data-filled formset and errors.
"""
messages.error(self.request, "Error dummy")
return self.render_to_response(self.get_context_data(formset=formset))
My Template
<form class="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<div class="">
{% for field in form %}
{{ field }}
{% endfor %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Save</button>
</form>