Django CreateView form not getting inserted - django

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

Related

How to implement an autocomplete-datalist form field inside a CreateView in Django?

I am very new to web development and specifically using Django Framework.
I am trying to find a clean, efficient and non external package dependant implementation for an autocomplete-datalist form field inside a Generic class based CreateView template in Django.
I have found numerous resources on various implementations, but most of them depend on external packages(autocomplete-light, jqueryCDN, etc.) and none of it is based on a class based generic CreateView.
I have been experimenting and I have managed to make the autocomplete-datalist work in a way but I am stuck in a very simple problem when I try to post my form with the data from the datalist element.
I get a validation error:
"city_name: This field is required"
I also noticed that the city object queried from the database inside the datalist has also the id of the city_name
models.py
from django.db import models
class City(models.Model):
name = models.CharField(max_length=50)
class Meta:
verbose_name_plural = "cities"
ordering = ['name']
def __str__(self):
return self.name
class Person(models.Model):
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
address = models.CharField(max_length=150)
city_name = models.ForeignKey(City, on_delete=models.CASCADE)
def __str__(self):
return f'{self.first_name} {self.last_name}'
views.py
from django.views.generic import ListView, CreateView
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Person, City
from .forms import PersonForm
# Create your views here.
class PersonList(LoginRequiredMixin, ListView):
model = Person
template_name = "home.html"
paginate_by = 20
login_url = "/login/"
redirect_field_name = 'redirect_to'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
class PersonCreate(LoginRequiredMixin, SuccessMessageMixin, CreateView):
model = Person
template_name = "testform.html"
login_url = "/login/"
form_class = PersonForm
success_url = 'testapp/add/'
success_message = 'Person registered successfully!'
redirect_field_name = 'redirect_to'
forms.py
from django import forms
from .models import Person, City
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ["first_name", "last_name", "address", "city_name"]
testform.html
{% extends 'home.html' %}
{% load static %}
{% block content %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-success alert-dismissible fade show" role="alert">
<span style="font-size: 18px;padding: 1mm"><i class="fa-solid fa-circle-check"></i></span>{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<form method="POST">
{% csrf_token %}
<div class="mb-3">
<label for="first_name charfield" class="form-label"> First Name</label>
{{form.first_name}}
</div>
<div class="mb-3">
<label for="last_name charfield" class="form-label">Last Name</label>
{{form.last_name}}
</div>
<div class="mb-3">
<label for="address charfield" class="form-label">Address</label>
{{form.address}}
</div>
<div class="mb-3">
<label for="city_name datalist" class="form-label">City Name</label>
<input type="text" list="cities" class="form-control">
<datalist id="cities">
{% for city in form.city_name %}
<option>{{ city }}</option>
{% endfor %}
</datalist>
</div>
<button class="btn btn-outline-primary" type="submit">Submit</button>
</form>
{{form.errors}}
{% endblock %}
Result:
I believe it is a necessary feature for all modern web applications to have this kind of functionality within their database query-autocomplete form fields system. It is a pity that although Django provides this feature for the AdminModels through its autocomplete_fields attribute, it makes it so hard to implement on Generic Class Based Views on the actual application models.
How can I approach this issue, and is there a efficient and more optimized way to implement it?
If you don't want a field required you can set the attribute blank=True in the model class. A question I have is why would you want to have a Foreignkey to just a city name. Or are you trying to use the a list of cities to populate the drop down? In that case the Foreign Key is definitely not the answer.

How do I modify existing fields in django that have a many to many relationship?

I have a model with a many to many field (participant field in my Activity model, see below) that I want to edit using createview. However, I can only choose from the already existing entries, not create new ones. It is possible to do that in the admin site so there is a solution but I can't figure it out. I have tried to modify form_valid in views but with no succes. Any ideas how to add or modify a field that has a many to many relation?
views.py:
#response=super(CreateTour,self).form_valid()
#self.object.project=self.form.cleaned_data['participant']
class CreateTour(CreateView):
form_class=CreateTourForm
template_name='artdb/createtour.html'
def get_context_data(self,**kwargs):
context=super(CreateTour,self).get_context_data(**kwargs)
context['formset']=CreateFormset()
return context
def form_valid(self,form):
self.object=form.save(commit=False)
for p in form.cleaned.data['participant']:
ap=Person()
ap.group=self.object
ap.person=ap
ap.save()
return super(self).form_valid(form)
models.py:
class Activity(models.Model):
activity_name=models.CharField(max_length=200,default='no name')
project=models.ForeignKey(Project,on_delete=models.CASCADE,default=1)
participant=models.ManyToManyField(Person)
min_stage_area=models.IntegerField(default='2')
light_requirements=models.CharField(max_length=200,default='no requirements')
sound_engineer=models.CharField(max_length=200,default='not needed')
comment=models.ManyToManyField(Comment)
def __str__(self):
return self.activity_name
class Meta:
ordering = ('activity_name',)
forms.py:
class CreateTourForm(ModelForm):
class Meta:
model=Activity
fields=('activity_name','project','participant')
widgets={'participant':CheckboxSelectMultiple,}
CreateFormset=modelformset_factory(
Activity,
fields=['activity_name','participant'],
extra=1,
widgets={
'date':DateInput(attrs={'type': 'date'}),
}
)
template:
{% extends "artdb/index.html" %}
{% block ct %}
<form method="post">{% csrf_token %}
<div class="input-group">
{% for fr in formset %}
{{fr}}
{% endfor %}
<a>
{{form}}
</a>
<div class="input-group-append">
<button class="btn btn-success add-form-row">+</button>
</div>
</div>
<hr></hr>
<div class="row spacer">
<input type="submit" value="save">
</div>
</form>
{% endblock ct %}

Can't manage to display form from my view

I'm trying to create a page where admins can upload some files using some FileField. The problem is that I can't manage to display any field from my form, I must be missing something important but I can't find out what, hope anyone can help me.
Here is the code related to this form:
urls.py
urlpatterns = patterns(
'',
url(r'^admin_fichiers_phyto/$', phyto_views.AdminFichiersPhyto.as_view(), name='phyto-admin-fichiers-phyto'),
)
phyto_admin_fichiers.html
{% block forms %}
{% if user.is_staff%}
<fieldset>
<div>
<span>{{ form.other }}</span>
</div>
</fieldset>
<p>
<input id="submit" class="btn btn-primary" type="submit" value="Synchronisation Autre" name="autre"/>
<input id="submit" class="btn btn-primary" type="submit" value="Synchronisation Traitements généraux" name="trtm_gen"/>
</p>
{% endif %}
{% endblock %}
views.py
class AdminFichiersPhyto(TemplateView):
template_name = 'phyto/phyto_admin_fichiers.html'
form_class = forms.PhytoFileForm
current_url = 'phyto-files'
context_object_name = 'phyto_files'
def post(self, request, *args, **kwargs):
if request.POST.get('autre'):
return HttpResponse('<h1>autre</h1>')
if request.POST.get('trtm_gen'):
return HttpResponse('<h1>Traitement Generaux</h1>')
forms.py
class PhytoFileForm(forms.Form):
class Meta:
model = models.PhytoFile
fields = ['general_treatment', 'other']
def __init__(self, *args, **kwargs):
super(PhytoFileForm, self).__init__(*args, **kwargs)
models.py
class PhytoFile(models.Model):
general_treatment = models.FileField(upload_to='fichiers_phyto/', blank=True, null=True)
other = models.FileField(upload_to='fichiers_phyto/', blank=True, null=True)
Here is what my webpage is showing :
https://imgur.com/a/yH0be0K
I can't understand why the Field isn't displayed, I really hope somebody have the knowledge to help me with my problem ! ^_^
Have a nice day ! :D
You have several major issues here.
TemplateView doesn't know anything about form_class (or context_object_name, for that matter). And by defining post like that you've avoided actually doing anything with the uploaded data. You need to use a view that deals with forms; in your case, a CreateView would be ideal. Inside that view, that response code needs to go in form_valid.
Secondly, PhytoFileForm needs to inherit from forms.ModelForm; a standard Form class doesn't know anything about models and doesn't use a Meta class. However, since you're not customizing the form beyond the field selection, you can just rely on the automatic form created by CreateView.
Next, your template is broken; it doesn't have an HTML form element. Also, since your form class has two fields but you only display one on the template, the form will never be valid.
So, putting it together:
class AdminFichiersPhyto(CreateView):
template_name = 'phyto/phyto_admin_fichiers.html'
model = models.PhytoFile
def form_valid(self, form):
form.save()
if request.POST.get('autre'):
return HttpResponse('<h1>autre</h1>')
if request.POST.get('trtm_gen'):
return HttpResponse('<h1>Traitement Generaux</h1>')
{% block forms %}
{% if user.is_staff%}
<form method="post" action="" enctype="multipart/form-data">
<fieldset>
<div>
<span>{{ form.other }}</span>
</div>
<div>
<span>{{ form. general_treatment }}</span>
</div>
</fieldset>
<p>
<input id="submit" class="btn btn-primary" type="submit" value="Synchronisation Autre" name="autre"/>
<input id="submit" class="btn btn-primary" type="submit" value="Synchronisation Traitements généraux" name="trtm_gen"/>
</p>
</form>
{% endif %}
{% endblock %}

ImproperlyConfigured - Django

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/'

Refering to related object in ListView

I am trying to create some kind of 'panel control'. I need to manage my objects directly from ListView. I'll show you transparent (I believe) schema what I'm trying to solve.
Models
class Category(models.Model):
cat_name = models.CharField(max_length=120)
class Product(models.Model):
category = models.ForeignKey(Category)
prod_name = models.CharField(max_length=120)
Views
class CategoryListView(ListView):
model = Category
def get_context_data(self, *args, **kwargs):
context = super(CategoryListView, self).get_context_data(*args, **kwargs)
# two class based forms created for models
context['form_category'] = CategoryForm()
context['form_product'] = ProductForm()
return context
def post(self, request, *args, **kwargs):
form_category = CategoryForm(request.POST or None)
if form_category.is_valid():
new_category = form_category.save(commit=False)
new_category.save()
return redirect('category_list')
return Http404
templates
I use two modal window to open my forms which I added to my context views
<button>Modal btton opens product form</button>
{% for category in object_list %}
<div class="panel">
<div class="panel-heading">
<h3>{{ category.cat_name}}</h3>
<button>Modal btn opens product form</button>
</div>
<div class="panel-body">
<ul>
{% for product in category.product_set.all %}
<li>
<p>{{ product.prod_name }}<p>
<span>Delete</span>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
<form action="." method="POST">{%csrf_token%}
{{ form_category.as_p }}
<input type="submit" value="add category">
</form>
<form action="." method="POST">{%csrf_token%}
{{ form_product.as_p }}
<input type="submit" value="add product">
</form>
For first form (CategoryForm) it's working fine because whole page refers to that (model = Category). Now is the question - how to create second object on that page. I can do this in DetailView using urls and parameter like slug, id or pk and after passing it as value but I want to do this from that page without moving to detail page.
Secondly I was thinking how to delete products from list but there is the same problem how to let django know that witch object I would like to remove.
Thanks.