Booleans as checkboxes and saving the form - django

I have created a OnetoOne model for some user preference checkboxes. This is mapped to the User, and using signals I create it when the user is created. Here is what I have so far:
Model:
class DateRegexes(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
prefix_1 = models.NullBooleanField()
prefix_2 = models.NullBooleanField()
prefix_3 = models.NullBooleanField()
prefix_4 = models.NullBooleanField()
prefix_5 = models.NullBooleanField()
prefix_6 = models.NullBooleanField()
prefix_7 = models.NullBooleanField()
prefix_8 = models.NullBooleanField()
prefix_9 = models.NullBooleanField()
#receiver(post_save, sender=User)
def create_date_regexes(sender, instance, created, **kwargs):
if created:
DateRegexes.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_date_regexes(sender, instance, **kwargs):
instance.date_prefs.save()
Form:
class DatePreferenceForm(forms.ModelForm):
class Meta:
model = DateRegexes`
View:
#login_required
def set_date_preferences(request):
if request.method == 'POST':
form = DatePreferenceForm(request.POST)
if form.is_valid():
form.save()
else:
date_prefs = get_object_or_404(DateRegexes, user=request.user)
form = DatePreferenceForm(instance = request.user.date_prefs)
return render(request, 'set_date.html', {'form': form})
Template:
{% extends 'base.html' %}
{% block title %}Date Preferences{% endblock %}
{% block content %}
{% if user.is_authenticated %}
<p>logout</p>
<form method="post">
{% csrf_token %}
{% for field in form.visible_fields %}
<div>here {{ field }}</div>
{% endfor %}
<button type="submit">Set</button>
</form>
</div>
{% else %}
<p>You are not logged in</p>
login
{% endif %}
{% endblock %}
When I execute this, the first thing that happens is that the expected checkboxes that are filled in according to their previously selected preferences, I get a bunch of dropboxes that each say:
"Unknown", "Yes", "No".
1) How can I get it show html checkboxes instead of the dropboxes?
2) When I submit the form I get:
IntegrityError at /db/set_date_preferences/
NOT NULL constraint failed: db_dateregexes.user_id
Which I understand that it is having trouble associating my form to the logged in user, but I'm not sure what the right way to associate this should be

You're using a NullBooleanField which can have 3 values: None, True or False. So the default widget to render such a field is a dropdown. If you want a checkbox you should decide what you want None to map to and override the fields in the DatePreferenceForm to be a BooleanField.
Second, since every user already has prefs you should initialise your form with the correct instance, even in the POST case. I don't really understand how your form can be valid since user is a required field (you should really exclude it from the form).
class DatePreferenceForm(forms.ModelForm):
class Meta:
model = DateRegexes
exclude = ['user']
# View
def set_date_preferences(request):
if request.method == 'POST':
form = DatePreferenceForm(request.POST, instance=request.user.date_prefs)
if form.is_valid():
form.save()
# would be good practice to redirect to a success page here
else:
date_prefs = get_object_or_404(DateRegexes, user=request.user)
form = DatePreferenceForm(instance = request.user.date_prefs)
return render(request, 'set_date.html', {'form': form})

Related

Django inlineformset_factory. Request.FILES not returning filename to form when formset.is_valid() = False

I'm trying to allow the user to upload images tied to a project. I'm doing this via an inlineformset_factory.
Im serving the form to the user trough a function based view. When the user fails to fill in one (or more) of the formsets correctly formset.is_valid() returns false and the bound formsets get returned to the user along with error messages. What i'm having trouble with is that the imagefield is not returned to the bound form. So if the user has filled in 2 out of 3 forms correctly then neither of the formsets have a bound imagefield after the failed validation. I can't manage to figure out if this is working as intended or if i'm doing something wrong. If i print out request.FILES i get the following:
<MultiValueDict: {'image_set-0-filename': [<InMemoryUploadedFile: IMG_0017.jpg (image/jpeg)>], 'image_set-1-filename': [<InMemoryUploadedFile: sprite.svg (image/svg+xml)>]}>
I would like the imagefield to be bound and show the user what value the field has.
before incorrect form submission
after incorrect form submission
Hopefully you can see where my mistake is or tell me if i'm thinking about this the wrong way.
Thank you in advance
forms.py
class EndUserImageForm(forms.ModelForm):
class Meta:
model = Image
fields = ["filename", "description"]
widgets = {
"filename": forms.FileInput(attrs={"class": "form__field no--outline"}),
"description": forms.Textarea(attrs={"class": "form__field no--outline", "placeholder": "Description"})
}
EndUserImageInlineFormset = inlineformset_factory(
Project, Image, form=EndUserImageForm, extra=2)
views.py
def image_formset(request, pk):
project = get_object_or_404(Project, pk=pk)
image_formset = EndUserImageInlineFormset(request.POST or None, request.FILES or None)
context = {
"image_formset": image_formset
}
if request.method == "POST":
if image_formset.is_valid():
images = image_formset.save(commit=False)
for image in images:
image.project = project
image.save()
return HttpResponseRedirect(reverse("projects:home"))
return render(request, "projects/test.html", context)
template
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<h4>Add Images</h4>
{{ image_formset.non_form_errors }}
{{ image_formset.management_form }}
<div id="image-form-list">
{% for form in image_formset %}
{{ form.errors }}
<div class="image_form">
{{ form.filename }}
{{ form.description }}
</div>
{% endfor %}
</div>
<input type="submit" value="Save" class="button button--main activate"/>
</form>
models.py
class Image(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
filename = models.ImageField(upload_to=project_directory_path, blank=False)
description = models.CharField(max_length=350, blank=True)
created = models.DateTimeField(auto_now_add=True)
def image_formset(request, pk):
project = get_object_or_404(Project, pk=pk)
image_formset = EndUserImageInlineFormset()
if request.method == "POST":
image_formset = EndUserImageInlineFormset(request.POST or None, request.FILES or None)
if image_formset.is_valid():h
images = image_formset.save(commit=False)
for image in images:
image.project = project
image.save()

Tags are not being stored in the database even after saving form in django

views.py
def post(request):
if request.method == 'POST':
form = PostModelForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
# using the for loop i am able to save the tags data.
# for tag in form.cleaned_data['tags']:
# post.tags.add(tag)
images = request.FILES.getlist('images')
for image in images:
ImagesPostModel.objects.create(post=post, images=image)
return redirect('/Blog/home/')
else:
form = PostModelForm(request.POST)
return render(request, 'post.html', {'form': form})
models.py
class PostModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date_time = models.DateTimeField(auto_now_add=True)
title = models.TextField(null=True)
body = models.TextField(null=True)
tags = TaggableManager()
def __str__(self):
return str(self.user)
post.html
{% extends 'base.html' %}
{% block content %}
<form action="{% url 'post' %}" enctype="multipart/form-data" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="file" multiple name="images">
<input type="submit">
</form>
{% endblock %}
After giving the input the data is stored in the tags field but, not saving in the database.
I can manually insert data through the admin panel successfully but not as a non-staff user.
I have installed taggit and placed it in the installed_apps in settings.py.
Tags are being saved using post.tags.add(tag) inside for loop. What is the issue with the code?
This is because you use commit=False for the form: then the form has no means to save the many-to-many fields. It is also not necessary to do that, you can work with:
def post(request):
if request.method == 'POST':
form = PostModelForm(request.POST)
if form.is_valid():
form.instance.user = request.user # set the user
post = form.save() # save the form
ImagesPostModel.objects.bulk_create([
ImagesPostModel(post=post, images=image)
for image in request.FILES.getlist('images')
])
return redirect('/Blog/home/')
else:
form = PostModelForm()
return render(request, 'post.html', {'form': form})
Note: Models normally have no Model suffix. Therefore it might be better to rename PostModel to Post.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

View didn't return an HttpResponse

My project is here github Project
I'm getting this error.
ValueError at /salesapp/add/ashton-aged-maduro/
The view salesapp.views.add_CartItem didn't return an HttpResponse object. It returned None instead.
I get this error when I click the 'Add to Cart' button on my singleproduct.html template which calls the ProductAddToCart form. The view is add_CartItem.
I also get the error Field is required when I don't initially set the form values. I'm just stuck now.
This is models.py
class Product(models.Model):
itemid = models.CharField(max_length=128, unique=True)
itemname = models.CharField(max_length=128)
brand = models.CharField(max_length=128)
image = models.ImageField(upload_to='static/images/')
notes = models.CharField(max_length=250)
price = models.IntegerField()
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.itemname)
super(Product, self).save(*args, **kwargs)
def __str__(self):
return self.itemname
class CartItem(models.Model):
cart_id = models.CharField(max_length=50)
date_added = models.DateTimeField(auto_now_add=True)
quantity = models.IntegerField(default=1)
itemid = models.ForeignKey('Product', unique=False)
class Meta:
db_table = 'cart_items'
ordering = ['date_added']
def name(self):
return self.product.name
My forms.py
class ProductAddToCartForm(forms.ModelForm):
cart_id = forms.CharField(max_length=50)
date_added = forms.DateTimeField()
quantity = forms.IntegerField()
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
#itemid = models.ForeignKey('Product', unique=False)
class Meta:
model = CartItem
fields = ('cart_id', 'date_added', 'quantity', 'slug', 'itemid', )
My views.py
def add_CartItem(request, product_name_slug):
print('In add_CartItem --------------------')
form = ProductAddToCartForm(request.POST)
p = Product.objects.get(slug=product_name_slug)
form = ProductAddToCartForm(initial={'cart_id': 123, 'date_added':date.date.today(), 'quantity': 1, 'slug':p.slug, 'id':p.id, 'itemid':p.itemid})
form.save(commit=False)
print(form)
print(p.slug)
print(p.id)
print(p.itemid)
if form.is_valid():
print(p)
print('In form.is_valid()--------------------------------')
ci = CartItem.objects.create(cart_id=1, date_added=date.date.today(), quantity=1, itemid=p)
form.save(commit=True)
return index(request)
else:
print(form.errors) #return render(request, 'salesapp/errors.html', {'form': form})
My urls.py
from django.conf.urls import url
from salesapp import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'about/', views.about, name='about'),
url(r'customers/', views.customers, name='customers'),
url(r'products/', views.products, name='products'),
url(r'^add_product/$', views.add_product, name='add_product'),
url(r'^add_customer/$', views.add_customer, name='add_customer'),
url(r'items/(?P<product_name_slug>[\w\-]+)/$', views.show_product, name='show_product'),
url(r'^add/(?P<product_name_slug>[\w\-]+)/$', views.add_CartItem, name='add_CartItem'),
#url(r'^cart/$', views.show_cart, name='show_cart'),
#url(r'^register/$', views.register, name='register'),
#url(r'^login/$', views.user_login, name='login'),
#url(r'^logout/$', views.user_logout, name='logout'),
#url(r'^restricted/', views.restricted, name='restricted'),
]
and my template where I want to display the ProductAddToCartForm but add a product to the CartItem table.
<!DOCTYPE html>
{% extends 'salesapp/base.html' %}
{% load staticfiles %}
{% block title_block %}
{{ product.itemname }}
{% endblock %}
{% block body_block %}
<div>
<div>
<ul style="list-style:none; text-align:center;">
<li style="float:left; width:25%; margin:20px;">
<img src="/{{ product.image }}"/>
<div>
<b>{{ product.itemname }}</b><br/>
Price per cigar:<br/>
<b>${{ product.price }}</b>
<p>{{ product.notes }}</p>
</div>
<form method="post" action="/salesapp/add/{{ product.slug }}/" class="cart">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<br />
<input type="submit" value="Add To Cart" name="submit" alt="Add To Cart" />
</form>
<div class="cb"></div>
</li>
</ul>
</div>
<!-- More code -->
</div>
{% endblock %}
A view function in django should either return a json or a dict or can return a Webpage
You can either do one of following
1) return a json or dict
return {"value1":"value","value2":"value"}
2)redirect to a web page
return redirect('/some/url/')
3)return Http response
return HttpResponse(status=<status code>,content="some content")
4)render a template
t = loader.get_template('myapp/index.html')
c = {'foo': 'bar'}
return HttpResponse(t.render(c, request),
content_type='application/xhtml+xml')
Because you did not return any response to the view,
According to the docs
A view function is simply a Python function that
takes a Web request and returns a Web response.
You need to return to use render method for initial rendering of form and for redirection to another view you can use redirect method of Django.
A view function must return an HttpResponse. For example, if the process was successfull and you dont want to return anything, you can return HttpResponse(status=200)
When a view handles forms, you have to split GET and POST requests. In the GET part you need to instantiate the form without data. In the POST part you fill the form with request.POST data. And this data must have ALL mandatory fields. A typical view function scheme to handle a form is the folowing:
def view(request):
if request.method == "GET":
form = MyForm()
return ...
if request.method == "POST":
form = MyForm(request.POST)
form.save()
return ...
In your template, you have to show all form fields. Then, all form fields will be passed with the request. If you dont, you need to fill the form fields in the view.
first delete the prints, almost when you make a question, are useless in Django
def add_CartItem(request, product_name_slug):
form = ProductAddToCartForm(request.POST)
if request.method == 'POST':
if form.is_valid():
ci = CartItem.objects.create(cart_id=1, date_added=date.date.today(), quantity=1, itemid=p)
ci.save()#you save your model changes, not your form
return HttpResponseRedirect(reverse('your:url'))#your return the success url or the same
else:
#here error, if form isn't valid
else:
form = ProductAddToCartForm(request.POST)
return render(request, 'your/template.html', {'form': form})
That is the correct way to work with forms in Django, first you must make a if statement asking to the browser if is a post request or a normal request, if is post request, take the data from the forms and are adding to the database, if not, Django return a empty template form.
Let me know if you problem solve

Can't get data in edit form

I'm trying to create an edit form for existing users, I have the User model and I associated to it a profile.
The problem is that the fields of profile are empty in the rendered html, however when I created a new user I filled these fields, and when I enter to administration I find the fields are filled.
models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
DEPARTMENT_CHOICES = (('MI', 'Math et info'),
('ST', 'Science et Tech'),
('SM', 'Science de la matiere'))
user = models.OneToOneField(User, on_delete=models.CASCADE)
teacher = models.BooleanField(default=False)
description = models.TextField(blank=True)
department = models.CharField(max_length=35, choices=DEPARTMENT_CHOICES, blank=True)
picture = models.ImageField(upload_to='profile-images', blank=True)
def __str__(self):
return self.user.username
views.py
def profile_view(request):
if request.method == 'POST':
user_form = EditUserForm(request.POST, instance=request.user)
ins = Profile.objects.get(pk=5)
profile_form = EditProfileForm(request.POST, request.FILES, instance=ins)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
profile.save()
return redirect(home)
else:
user_form = EditUserForm(instance=request.user)
profile_form = EditProfileForm(request.FILES, instance=request.user)
return render(request, 'account/profile.html', {'user_form': user_form,
'profile_form': profile_form})
forms.py
class EditProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('description', 'department', 'picture', )
class EditUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email', )
profile.html
{% extends 'manhal/base.html' %}
{% load staticfiles %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-6">
<form method="post" enctype="multipart/form-data" action="{% url 'profile' %}" class="form-horizontal">{% csrf_token %}
<fieldset>
<legend>User Profile</legend>
{{ user_form|crispy }}
{{ profile_form|crispy}}
<input type="submit" value="Save" class="btn btn-primary">
</fieldset>
</form>
</div>
{% endblock %}
First, your if/else block is checking if the request is a POST. Since the else block is not a POST, you do not want to pass any POST data into your form. This will make the form think it's bound with no data.
Also, it looks like you are passing the request.user to your ProfileForm as the instance, but the model on the ProfileForm meta class is expecting a Profile object.
Can you fix those two things and see if it works or not? If it doesn't work, please post some more code (like your templates).

Django inline-formset with an image field not updating

I have a listing model and a photo model:
class Listing(models.Model):
title = models.CharField(max_length=255)
<more fields>...
class Photo(models.Model):
image = models.ImageField(upload_to=create_file_path)
listing = models.ForeignKey(Listing, related_name='photos')
I am using a CBV, UpdateView, to edit a listing. I am using this form:
class ListingDetailForm(forms.ModelForm):
class Meta:
model = Listing
exclude = []
and the inline formset in forms.py to make deleting/changing the image possible:
PhotoFormset = inlineformset_factory(Listing, Photo, fields='__all__', extra=1)
here is my view:
class ListingDetailView(UpdateView):
model = Listing
template_name = 'listing/listing_detail.html'
form_class = ListingDetailForm
success_url = '/store/'
def get_context_data(self, **kwargs):
self.object = self.get_object()
context = super(ListingDetailView, self).get_context_data(**kwargs)
if self.request.POST:
context['form'] = ListingDetailForm(self.request.POST, instance=self.object)
context['photo_form'] = PhotoFormset(self.request.POST, self.request.FILES, instance=self.object)
else:
context['form'] = ListingDetailForm(instance=self.object)
context['photo_form'] = PhotoFormset(instance=self.object)
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
photo_form = PhotoFormset(self.request.POST)
print photo_form.is_valid()
if form.is_valid() and photo_form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
print 'in form valid for update'
context = self.get_context_data()
base_form = context['form']
photo_form = context['photo_form']
# print base_form
# print photo_form
if base_form.is_valid() and photo_form.is_valid():
print 'forms are valid for update'
base_form.save()
photo_form.save()
return super(ListingDetailView, self).form_valid(form)
else:
return self.render_to_response(self)
and the relevant template section:
{% block body %}
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}<br><br>
{% endfor %}
{% for field in photo_form %}
{{ field.errors }}
{{ field.label_tag }} {{ field }}<br><br>
{% endfor %}
{{ photo_form.management_form }}
<input type="submit" value="Update" />
</form>
{% endblock %}
The issues I am having are:
1) If there is a photo attached to the listing, through the admin, the photo form does not pass validation if I do nothing with the photo form, e.g. change only fields from the listing model. The photo form displays no errors when the page reloads after invalid.
2) selecting a new photo does not change the current photo, the photo form does not validate and displays no errors.
3) if there is currently no photo related to the listing trying to add one validates through the form but does not actually save a photo related to that listing.
Deleting an image, if there is one attached to the listing, works just fine. Deleting the image and updating some other field from the listing works. If there is no image updating only a listing field works. Adding a second photo to the listing through the form does not work and displays no form errors.
There are a few issues I noticed with your form.
You need to include enctype="multipart/form-data" on your form attributes or else you won't be able to post file data to the server
I would use the Django methods for rendering the form (form.as_p, form.as_table or form.as_ul) if you absolutely need to use manual rendering then follow the official guide: model formsets
On the post method your formset is missing FILES and instance
Once you implement these changes your formset should work just fine.