I'm using a class based generic view, specifically the UpdateView. I have other objects that I have currently which does CRUD as expected.
Now, I created a new object which also uses the generic class based views. My issue right now is that when I submit an update form, I can't exactly trace what happens when I press that submit button on an update form.
it is pointing to go on this url
http://localhost:8000/order/question_update/
but I don't have a question_update url that doesn't have a parameter. I have a question_update url that has a parameter at the end.
from my urls.py
path('question_update/<int:pk>', views.QuestionUpdate.as_view(), name='question_update'),
and also the success url is not pointing to the question_update url above.
How do I know what does the generic class UpdateView does when I hit the submit button? I'd just like to trace where it's getting the URL which I did not declared anywhere.
I did a text search on my codes and this url is not declared at all.
here are the other URLs
path('question_list/<int:file_id>/', views.QuestionList.as_view(), name='question_list'),
path('question_create_param/<int:file_id>/', views.QuestionCreate.as_view(), name='question_create_param'),
path('question_update/<int:pk>', views.QuestionUpdate.as_view(), name='question_update'),
here's my view for update function
class QuestionUpdate(LoginRequiredMixin, UpdateView):
login_url = 'login'
model = Question
form_class = QuestionForm
def get_form(self, data=None, files=None, **kwargs):
kwargs['pk'] = self.kwargs['pk']
return QuestionForm(data, files, **kwargs)
def get_success_url(self):
messages.success(self.request, 'Question updated')
obj = get_object_or_404(Question, pk=self.kwargs['pk'])
return reverse_lazy('file_status:question_list', args=[obj])
and this is my template
{% extends "base.html" %}
{% load bootstrap3 %}
{% block content %}
<h1>Create/Update</h1>
<form id="question_form" method="post" action="." class="form">
{% csrf_token %}
{% bootstrap_form form layout='vertical' %}
{% buttons %}
<button type="submit" class="btn btn-primary">Submit</button>
{% endbuttons %}
</form>
{% endblock %}
here's my model as well
class Question(models.Model):
file = models.ForeignKey(file, on_delete=models.CASCADE)
query_type = models.CharField(max_length=255, null=True)
date_sent = models.DateField(auto_now=False, auto_now_add=False, null=True)
date_completed = models.DateField(auto_now=False, auto_now_add=False, null=True)
responsible = models.CharField(max_length=255, null=True)
status = models.CharField(max_length=255, null=True)
remarks = models.CharField(max_length=255, null=True)
def __str__(self):
return str(self.file.id)
def get_absolute_url(self):
return reverse('file_status:question_detail', args=[str(self.id)])
It appears some tags in the form should not be there in the first place. What I did was get back to the documentation in django projects and identify how it should be declared properly.
My form starts with this:
<form id="question_form" method="post" action="." class="form">
But it should only be this:
<form id="question_form" method="post">
I'm not entirely sure why action and class tags are causing errors on submit of an update form, if you know please enlighten me. Another weird thing is that I have those 2 extra tags on my other forms but it only happens to fail on the UpdateView for the question object.
Also this was patterned from cookiecutter django crud.
Related
I am making a django project and I have a form for the User to add a Vehicle Manually that will be assigned to him. I also would like to had an option for the user to choose a vehicle based on the entries already present in the database.
vehicles/models.py
class Vehicle(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
nickname = models.CharField(unique = True, max_length=150)
date_joined = models.DateTimeField(default=timezone.now)
brand = models.CharField(max_length=150)
battery = models.CharField(max_length=150)
model = models.CharField(max_length=150)
def __str__(self):
return self.nickname
def get_absolute_url(self):
return reverse('vehicle-list')
class Meta:
db_table = "vehicles"
I created a form so the user can add his Vehicles as such:
vehicles/forms.py
class VehicleAddFormManual(forms.ModelForm):
class Meta:
model = Vehicle
fields = ('brand','model', 'battery', 'nickname')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
self.fields['brand']
self.fields['model']
self.fields['battery']
self.fields['nickname']
The corresponding view:
vehicles/views.py
class AddVehicleViewManual(LoginRequiredMixin, CreateView):
model = Vehicle
form_class = VehicleAddFormManual
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
The html file:
vehicles/templates/vehicles/vehicle_form.html
{% extends "blog/base.html" %}
{% block content %}
{% load crispy_forms_tags %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Vehicle</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Submit</button>
</div>
</form>
</div>
{% endblock content %}
I would like to add another form in which the user has a dropdown with option with the brands, models and batteries that already exist in the database. If there's a car in the database with brand: Tesla, model: Model 3, battery: 50 kWh, then it would appear in the dropbox as a choice for each field.
I'm not sure how to do this and sorry for the newbie question... Thanks in advance!
I once had to do something similar, but I needed a form which had one checkbox for each item in a list of externally-supplied strings. I don't know if this is the cleanest way, but I used python metaclasses:
class SockSelectForm(forms.Form):
#staticmethod
def build(sock_names):
fields = {'sock_%s' % urllib.parse.quote(name):
forms.BooleanField(label=name, required=False)
for name in sock_names}
sub_class = type('DynamicSockSelectForm', (SockSelectForm,), fields)
return sub_class()
In my get() method, I instantiate it as:
form = SockSelectForm.build(names)
and the corresponding form handling in the post() method is:
form = SockSelectForm(request.POST)
I suspect if you look under the covers of Django's ModelForm, you'd see something similar, but I couldn't use ModelForm because it's too closely tied to the model system for what I needed to do.
model.py
class DropdownModel(models.Model):
brand = models.CharField(max_length=150)
battery = models.CharField(max_length=150)
model = models.CharField(max_length=150)
def __str__(self):
return self.brand.
form.py
from .models import DropdownModel
all_brand = DropdownModel.objects.values_list('brand','brand')
all_battery = DropdownModel.objects.values_list('battery','battery')
all_model= DropdownModel.objects.values_list('model','model')
class DropdownForm(forms.ModelForm):
class Meta:
model = DropdownModel
fields = "__all__"
widgets = {
'brand':forms.Select(choices=all_brand),
'battery':forms.Select(choices=all_battery),
'model':forms.Select(choices=all_model),
}
view.py
from django.shortcuts import render
from .form import DropdownForm
# Create your views here.
def HomeView(request):
form = DropdownForm()
context = {'form':form}
return render(request,'index.html',context)
index.html
{% extends "base.html" %}
{% load static %}
{% block title %}
Index | Page
{% endblock title %}
{% block body %}
{{form.as_p}}
{% endblock body %}
Output-
Note- if u can't see updated values in dropdown do server restart because localhost not suport auto update value fill in dropdown it's supoorted on live server
Thank you
i have views.py like this
class InventoryListView(ListView):
context_object_name = 'inventorys'
model = models.Inventory
def get_context_data(self, **kwargs):
context = super(InventoryListView, self).get_context_data(**kwargs)
context['form'] = InventoryForm()
return context
class InventoryCreateView(CreateView):
fields = ('name', 'sn', 'desc', 'employee')
model = models.Inventory
and here my models.py
class Inventory(models.Model):
name = models.CharField(max_length=255)
sn = models.DecimalField(max_digits=20, decimal_places=0)
desc = models.TextField(blank=True, null=True, default='Empty')
employee = models.ForeignKey(Employee, null=True, on_delete=models.SET_NULL, related_name='employee')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True, null=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("system:inventory_list")
and here my urls.py
url(r'^invn/$',views.InventoryListView.as_view(), name='inventory_list'),
url(r'^invn/create$',views.InventoryCreateView.as_view(), name='inventory_create'),
and here my inventory_list.html
{% for inventory in inventorys %}
<td>{{ inventory.name }}</td>
{% endfor %}
<form method="post" action="{% url 'system:inventory_create' %}">
{% csrf_token %}
{{ form.as_p }}
</form>
if the form is valid, its work well.
But if the form is invalid it will redirect to inventory_form.html that will cause TemplateDoesNotExist since Django CreateView need inventory_form.html (CMIIW).
simple solution create inventory_form.html but that not what I want.
how to make it redirect to my ListView when the form is invalid with error messages ?...
It's easy to redirect to ListView.
Just override form_invalid() in CreateView.
Default form_invalid() is below
def form_invalid(self, form):
"""If the form is invalid, render the invalid form."""
return self.render_to_response(self.get_context_data(form=form))
You can redirect to your ListView.
But the problem is, when redirect, you cannot pass any context or message to them. It just redirects to your listview.
So you have few choices.
First, you can keep form_invalid() the same, and if form is invalid (you can check it with error with form.errors) show alert error message in the CreateView page with alert or confirm, and when click ok, redirect to listview page.
Second, you can use session. Before redirect to listview, you can give some session data for deciding it's from invalid CreateView.
I think first one is better approach for web.
If you have any question, just comment it.
Possible to redirect User inside form_invalid() method, and use the Django Message Framework, to alert the user (Error Message).
from django.contrib import messages
from django.shortcuts import redirect
class InventoryCreateView(CreateView):
fields = ('name', 'sn', 'desc', 'employee')
model = models.Inventory
def form_invalid(self, form):
'form is invalid'
messages.add_message(self.request,messages.WARNING,"Form is invalid")
return redirect('inventory_list')
To retrieve the message in template:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
New to Django and Python and I need a little help with a foreign key drop down. Basically, I have a category model and a image model and I want users to be able to choose which category to put the image in. How do I create a drop down for the category in the image form? Are my views and html correct too? I have had a look online but I can't seem to do it myself. I keep getting errors.
Here are my models:
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=False)
img_name = models.CharField(max_length=120, blank=True)
img_date = models.DateTimeField(default=now())
img_user = models.ForeignKey(User)
img_cat_id = models.ForeignKey(Categories)
def __unicode__(self):
return self.img_name
class Categories(models.Model):
cat_descr = models.CharField(max_length =120, blank=False)
def __unicode__(self):
return self.cat_descr
VIEWS:
#login_required
def upload_images(request):
context = RequestContext(request)
context_dict={}
if request.method == 'POST': # render the form, and throw it back.
# take the form data and process it!
form = UploadImagesForm(request.POST, request.FILES)
if form.is_valid():
print 'form is_valid'
upload_image = form.save(commit=False)
upload_image.img_user = request.user
if 'image' in request.FILES:
upload_image.image =request.FILES['image']
upload_image.save()
return render(request, 'rmb/upload.html', {'upload_image': form})
else:
print form.errors
# Not a HTTP POST, so we render our form using two ModelForm instances.
# These forms will be blank, ready for user input.
else:
form = UploadImagesForm()
context_dict = {'upload_image': form}
all_categories = Categories.objects.order_by('-id')
context_dict['all_categories'] = all_categories
print context_dict
return render_to_response('rmb/upload.html', context_dict, context)
FORMS:
class UploadImagesForm(forms.ModelForm):
#cat_list = ModelChoiceField(queryset=Categories.objects.all())
class Meta:
model = Images
fields=('image','img_name')
HTML:
{% block body_block %}
<form id="upload_form" method="post" action="/rmb/upload/"
enctype="multipart/form-data">
{% csrf_token %}
{{ upload_image.as_table }}
<input type="submit" name="submit" value="Upload" />
{% for categories in all_categories %}
<div> {{ categories.id }} </div>
{{ categories.cat_descr }}
<input type="submit" name="submit" value="Upload" />
{% endfor %}
</form>
{% endblock %}
You don't need to insert the HTML for the form manually, just use {{form}} in the template.
{% block body_block %}
<form id="upload_form" method="post" action="/rmb/upload/"
enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
</form>
{% endblock %}
By default a ForeignKey will be a select field so you shouldn't need to do much else.
As an aside, give your models and fields more appropriate names. We know these are all image fields, because they are on the image and make sure, unless your model is a collection of things, you give it a singular name. Lastly, when using a Foreign Key and item gets an extra field of fieldname_id that is just the ID, whereas fieldname is the property that gives the related item as well.
So instead of:
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=False)
img_name = models.CharField(max_length=120, blank=True)
img_date = models.DateTimeField(default=now())
img_user = models.ForeignKey(User)
img_cat_id = models.ForeignKey(Categories)
Use:
class Image(models.Model):
image = models.ImageField(upload_to='images', blank=False)
name = models.CharField(max_length=120, blank=True)
date = models.DateTimeField(default=now())
user = models.ForeignKey(User)
category = models.ForeignKey(Categories)
I am trying to create a simple CRUD with ModelForm. It works fine except that every time I edit, saving creates a new instance of the data. So i edit and get an extra row in DB instead of an updated one. I am at a loss as to how it knows to save an existing charity as it does not store the PK (id) as a hidden field in the form. That is how I always did it before trying to use the 'fabulous' ModelForm!
It's driving me nuts, I have read everything and as far as I can tell I am doing everything right.
Here is my code..
Model:
from django.db import models
from django.conf import settings
COUNTRY_CHOICES = settings.COUNTRIES
class Charities(models.Model):
charity_name = models.CharField(max_length=100)
country = models.CharField(max_length=4, choices=COUNTRY_CHOICES)
registration_number = models.CharField(max_length=100)
address1 = models.CharField(max_length=100)
address2 = models.CharField(max_length=100)
city = models.CharField(max_length=30)
zip = models.CharField(max_length=10)
phone = models.CharField(max_length=20)
email = models.EmailField()
charity_logo_image = models.CharField(max_length=100)
charity_banner_image = models.CharField(max_length=100)
charity_accepted = models.IntegerField()
def __str__(self):
return self.charity_name
def __unicode__(self):
self.charity_name
View:
def list(request):
charities = Charities.objects.all()
return render_to_response('charities_charity_list.html', {'charities': charities})
def add(request):
return add_or_edit(request)
def edit(request, charity_id):
return add_or_edit(request, charity_id)
def add_or_edit(request, charity_id=None):
print "ID = " + str(charity_id)
form = CharityForm(request.POST or None,
instance=charity_id and Charities.objects.get(pk=charity_id))
# Save new/edited student
if request.method == 'POST' and form.is_valid():
print form
form.save()
return HttpResponseRedirect('/charities/list/')
return render_to_response('charities_charity_edit.html', {'form': form})
Form:
class CharityForm(ModelForm):
class Meta:
model = Charities
Template:
{% extends "base.html" %}
{% block title %}Charities Add{% endblock %}
{% block content %}
<form method="post" action="/charities/add/" id="save"><table cellpadding="0">{{ form.as_table}}</table><input type="submit" value="Save"></form>
{% endblock %}
It doesn`t work because your template is always POSTing to the view that adds a new Charity. When you manually type a URL like /charities/edit/5, it creates the ModelForm with the right initial data, but then POSTs to /charities/add, thus creating a new instance. You need to POST to /charities/edit/5, for example. Take a look at the url template tag.
I suggest you use 2 templates, one for adding, another for editing. I know it may not be very DRY, but I believe it's clearer this way.
Add template:
{% extends "base.html" %}
{% block title %}Charities Add{% endblock %}
{% block content %}
<form method="post" action="{% url charities_app.views.add %}"><table cellpadding="0">{{ form.as_table}}</table><input type="submit" value="Save"></form>
{% endblock %}
Edit template:
{% extends "base.html" %}
{% block title %}Edit Charity{% endblock %}
{% block content %}
<form method="post" action="{% url charities_app.views.edit charity.id %}"><table cellpadding="0">{{ form.as_table}}</table><input type="submit" value="Save"></form>
{% endblock %}
You may also want to check the create_object and update_object generic views, they are very useful in simple cases like yours.
UPDATE The issue is solved, all the code you can see works.
Hello!
I have a ForeignKey relationship between TextPage and Paragraph and my goal is to make front-end TextPage creating/editing form as if it was in ModelAdmin with 'inlines': several fields for the TextPage and then a couple of Paragraph instances stacked inline. The problem is that i have no idea about how to validate and save that:
#login_required
def textpage_add(request):
profile = request.user.profile_set.all()[0]
if not (profile.is_admin() or profile.is_editor()):
raise Http404
PageFormSet = inlineformset_factory(TextPage, Paragraph, fields=('title', 'text', ), extra=5)
textpage = TextPage()
if request.POST:
textpageform = TextPageForm(request.POST, instance=textpage, prefix='page')
formset = PageFormSet(request.POST, instance=textpage, prefix='paragraphs')
# Saving data
if textpageform.is_valid():
textpageform.save()
if formset.is_valid():
formset.save()
return HttpResponseRedirect(reverse(consult_categories))
else:
textpageform = TextPageForm(instance=textpage, prefix='page')
formset = PageFormSet(instance=textpage, prefix='paragraphs')
return render_to_response('textpages/manage.html', { 'formset' : formset,
'textpageform' : textpageform,
}, context_instance=RequestContext(request))
I know it's a kind of code-monkey style to post code that you don't even expect to work but I wanted to show what I'm trying to accomplish. Here is the relevant part of models.py:
class TextPage(models.Model):
title = models.CharField(max_length=100)
page_sub_category = models.ForeignKey(PageSubCategory, blank=True, null=True)
def __unicode__(self):
return self.title
class Paragraph(models.Model):
article = models.ForeignKey(TextPage)
title = models.CharField(max_length=100, blank=True, null=True)
text = models.TextField(blank=True, null=True)
def __unicode__(self):
return self.title
Any help would be appreciated. Thanks!
UPDATE. Instance references added, but still doesn't work - results in a ValidationError on this string:
formset = PageFormSet(request.POST, instance=textpage, prefix='paragraphs')
Any ideas?
The updated code with instance references actually works fine! The problem was in the template: I forgot the ManagmentForm. Here is the template code:
{% extends "site_base.html" %}
{% block body %}
<form action="" method="post">
{{ textpageform.as_p }}
{{ formset.management_form }}
{% for form in formset.forms %}
<p>{{ form.as_p }}</p>
{% endfor %}
<input type="submit" value="Go" />
{% endblock %}
Hope this example helps newbies like me :)