Upload image via ModelForm in django - python

When I try to submit my form it says "This field is required."
for an image even though I provide the image and other details to it.
forms.py file
from django.forms import ModelForm
from .models import Status
class CreatePost(ModelForm):
class Meta:
model=Status
fields = ["username","text","privacy","image"]
models.py file
class Status(models.Model):
title=models.CharField(max_length=20,default="updated status")
username = models.ForeignKey('User',on_delete=models.CASCADE)
#username = models.CharField(max_length=20)
text = models.TextField(blank=True, null=True)
image = models.ImageField(upload_to="media/image",null=True)
time = models.DateTimeField(auto_now=True)
privacy = models.CharField(max_length=5, blank=True, null=True)
gid = models.IntegerField(blank=True, null=True)
dp = models.SmallIntegerField(blank=True, null=True)
class Meta:
#unique_together = (('username', 'dp'),)
#managed = False
db_table = 'status'
view.py
def create_post(request):
form=CreatePost(request.POST or None)
if request.method=="POST":
if form.is_valid():
instance=form.save(commit=False)
instance.time=time.time()
instance.save()
return redirect('post',)
return render(request,"uposts/createpost.html",{'form':form})
createpost.html
{% extends "friendsbook/structure.html" %}
{% block content %}
<form action="" method="post">
{%csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
{% endblock %}
What it said after clicking on save button
I am only taking 4 fields in form because all other fields can be null. For time field I took care of that in views.py by giving the time there.

You have to modify the template like this adding multipart/form-data:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="myfile">
<button type="submit">Upload</button>
</form>
and in views.py, you can access the uploaded file using request.FILES.

Related

Forigen Key not displaying in rendered model form

I am looking for guidance on where I am going wrong. My model form is rendering correctly however the author has no option to select or input. I suspect this is is because the author is a foreign key. How do I get around this so the form will display a list of users or allow to manually input a name. Any help would be greatly appreciated :)
models.py
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE,
related_name="workout_posts")
updated_on = models.DateTimeField(auto_now=True)
featured_image = CloudinaryField('image', default='placeholder')
content = models.TextField(default='SOME STRING')
excerpt = models.TextField(blank=True)
created_on = models.DateTimeField(auto_now_add=True)
class Meta:
# ordered created on field starting with newest first
ordering = ['-created_on']
def __str__(self):
return self.title
forms.py
class MakeWorkOutForm(forms.ModelForm):
class Meta:
model = Post
fields = '__all__'
views.py
def createWorkOut(request):
form = MakeWorkOutForm
context = {'form': form}
return render(request, "add-workout.html", context)
html
{% extends "base.html" %} {% block content %} {% load static %}
<h1>Create A Workout</h1>
<form action="" method="POST">
{% csrf_token %}
{{form}}
<input type="submit" name="submit">
</form>
{% endblock content %}

duplicate key value violates unique constraint "photo_photo_user_id_key" DETAIL: Key (user_id)=(102) already exists

I receive the error above whenever users want to change their already existing profile.
This is the views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.db import transaction
from .models import Photo
from .forms import PhotoForm
Create your views here.
#login_required
#transaction.atomic
def upload(request, pk=None):
instance = Photo.objects.get(pk=pk) if pk else None
context = dict(save_pk=pk or "")
if request.method == 'POST':
# Only backend upload should be posting here
context['backend_form'] = form = PhotoForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
# Uploads image and creates a model instance for it
context['posted'] = form.save(commit=False)
context['posted'].user = request.user
context['posted'].save()
instance = Photo.objects.get(pk=pk) if pk else None
else:
# Form demonstrating backend upload
context['backend_form'] = PhotoForm(instance=instance)
return render(request, 'photo/upload.html', context)
from the stack trace, the problem is coming from this line
context['posted'].save()
How do I go about it? I want the users to be able to change their profiles without trying to create a new instance afresh.
This is the HTML template
<main class="mt-5 pt-5 container">
<!--A standard form for sending the image data to your server -->
<div class="mt-5 pt-5 container bg-white">
<h3 class="text-center">Upload</h3>
<div class="mb-3">
<div class="mb-auto p-2 bd-highlight">
<form action="{% url 'upload' %}" method="post" enctype="multipart/form-data" class="text-center mt-5 ">
{% csrf_token %}
{{ backend_form }}
</div>
<div class="p-2 bd-highlight text-center">
<input type="submit" value="Upload" class="text-center xbtn btn btn-primary">
</div>
</form>
</div>
{% if posted %}
<div class="results">
{% if posted.errors %}
Errors: {{ posted.errors }}
{% else %}
<div class="uploaded_info">
<div class="img-responsive">
{% cloudinary posted.image THUMBNAIL %}
</div>
</div>
{% endif %}
</div>
{% endif %}
</div>
</main>
<footer class="footerofupload">
{% block footer %} {% include 'footer-min.html' %} {% endblock footer %}
</footer>
models.py
from django.db import models
from cloudinary.models import CloudinaryField
from xpro.models import User
# Create your models here.
class Photo(models.Model):
# Misc Django Fields
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, related_name='photo')
create_time = models.DateTimeField(auto_now_add=True)
title = models.CharField("Title (optional)", max_length=150, blank=True)
# Points to a Cloudinary image
image = CloudinaryField('image')
""" Informative name for mode """
def __unicode__(self):
try:
public_id = self.image.public_id
except AttributeError:
public_id = ''
return "Photo <%s:%s>" % (self.title, public_id)
Below is the user model
class User(AbstractUser):
GENDER_CHOICES = (
('O', 'Other'),
('M', 'Male'),
('F', 'Female'),
)
is_student = models.BooleanField(default=False)
is_school = models.BooleanField(default=False)
is_sponsor = models.BooleanField(default=False)
email = models.EmailField(unique=True)
email_confirmed = models.BooleanField(default=False)
phone_number = models.CharField(max_length=12)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default='O')
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, null=True, editable=False)
# avatar = models.ImageField(upload_to='media/avarta', blank=True, default= '/media/avarta/default.png', null=True,)
You have a OneToOneField between User and Photo as here:
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, related_name='photo')
This essentially means for one User there can only be one Photo (and vice-versa). If you want multiple photos for one user change it to a ForeignKey:
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='photos')
If you want only one photo for a user delete any previous photos before adding any new one:
Photo.objects.filter(user=request.user).delete()
context['posted'] = form.save(commit=False)

How to update ManytoManyField in UserChangeForm in Django?

I want to update user info with UserChangeForm and things go pretty well except for the ManyToManyField. When I render the page I can see that all user informations are displayed in correct order of each field like user's username will be in the username field but it's blank in manytomanyfield.
#model.py
class Department(models.Model):
name = models.CharField(max_length=100)
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_('Email Address'), unique=True)
department = models.ManyToManyField(Department)
# some other fields
# forms.py
class EditUserForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ['email', 'department', ..]
widgets = {'department': forms.CheckboxSelectMultiple()}
# view.py
def home(request):
template_name = "app/home.html"
edit_form = EditUserForm(instance=request.user)
if request.method == "POST":
edit_form = EditUserForm(request.POST, instance=request.user)
if edit_form.is_valid():
edit_form.save()
return JsonResponse({'success': True}, status=200)
else:
return JsonResponse({'error': edit_form.errors}, status=400)
return render(request, template_name, {'edit_form': edit_form})
# template
<form action="{% url 'home' %}" method="POST">
<div class="row">
{{edit_form.email}}
{{edit_form.first_name}}
{% for department in edit_form.department %}
<h6 id="checkbox">{{department.tag}} {{department.choice_label}}</h6>
{% endfor %}
</div>
</form>
here is the picture
As you can see the names and email are displaying inside the form field but why all checkboxes are empty? (Checkbox fields are department)
If you just want to render the field you don't need a for loop. You can just use {{edit_form.department}}. In case you need to modify each input field in CheckboxSelectMultiple you should loop through edit_form.department.field.choices.
For example:
{% for choice, value in edit_form.department.field.choices %}
<input type="checkbox" name="{{choice.instance.value}}" value="{{choice.instance.pk}}" id="id_{{choice.instance.value}}">
{% endfor %}
Note that this will work only in django 3.0 and newer.
you need pass the value and the name in the input in your template
{% for value, name in edit_form.fields.department.choices %}
<input type="checkbox" name="{{name}}" value="{{value}}" id="{{name}}">
{% endfor %}

Django form not validating when trying to update an existing row from database

I am working on my first Django project.
But I get following errors:
edit_file template
<form method="POST" action="{% url 'edit_file' file.id %}">
{% csrf_token %}
{{ form.errors }}
{{ form.non_field_errors }}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field.errors }}
{{ hidden_field }}
{% endfor %}
<div class="form-group row">
<label for="id_name" class="col-sm-3 col-form-label"> File Name </label>
<div class="col-sm-4">
{% render_field form.name|add_class:"form-control" %}
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">File Path</label>
<div class="col-sm-4">
{% render_field form.directory_path|add_class:"form-control" %}
</div>
</div>
<div class="form-group">
{% render_field form.script_code|add_class:"form-control" %}
<pre id="id_script_code" style="height: 40pc;">{{ form.script_code }}</pre>
</div>
<button type="submit" class="btn btn-success mr-2">Save Changes</button>
Back
</form>
Views.py
def edit_file(request, id):
instance = get_object_or_404(File, id=id)
if request.method == "POST":
form = EditFileForm(request.POST, instance=instance)
if form.is_valid():
print('Form validation => True')
form.save()
return HttpResponse('<h1> database updated! </h1>')
else:
print('Form validation => False')
file = File.objects.latest('id')
context = {'file': file, "form": form}
return render(request, 'edit_file.html', context)
else:
instance = get_object_or_404(File, id=id)
form = EditFileForm(request.POST or None, instance=instance)
file = File.objects.latest('id')
context = {'file': file, "form": form}
return render(request, 'edit_file.html', context)
forms.py
class EditFileForm(ModelForm):
# field_order = ['field_1', 'field_2']
class Meta:
print("forms.py 1")
model = File
fields = ('name', 'script_code', 'directory_path','version')
def clean(self):
print("forms.py 2")
cleaned_data = super(EditFileForm, self).clean()
name = cleaned_data.get('name')
print("cleaned data: ", cleaned_data)
Models:
Version id point to a version which contains multiple files.
class File(models.Model):
# Incrementing ID (created automatically)
name = models.CharField(max_length=40)
script_code = models.TextField() # max juiste manier?
directory_path = models.CharField(max_length=200)
version = models.ForeignKey('Version', on_delete=models.CASCADE)
class Meta(object):
db_table = 'file' # table name
class Version(models.Model):
# Incrementing ID (created automatically)
version_number = models.CharField(max_length=20)
pending_update = models.BooleanField(default=False)
creation_date = models.DateTimeField(auto_now_add=True, null=True, editable=False)
modification_date = models.DateTimeField(auto_now_add=True, null=True)
connecthor = models.ForeignKey('ConnecThor', on_delete=models.CASCADE)
def __str__(self):
return self.connecthor_id
The problem:
form.is_valid() keeps failing. My view returns one error.
*version: This field is required. But I don't know how to fix this. Users should only be able to update 3 of the 5 data fields. So there is no reason to show the PK or FK in the template.
You've included version in the list of fields in your form, but you aren't outputting it in the template so there is no means of providing it. Since the model field does not specify blank=True, it is a required field, hence the error.
If you don't want users to be able to modify this field, you should remove it from that list of fields under Meta.
You do not have version in your template. Your model field for version does not say it can have null values. Hence your form validation fails. Include it in your template or remove the field from EditFileForm class's Meta class in forms.py

Django - saving formset with ManyToMany

I've got the following problem. Once I try to save edited objects (details below), I'm getting error:
MultiValueDictKeyError at /apps/edit/1/
"Key 'application2server_set-0-id' not found in <QueryDict: {u'application2server_set-MAX_NUM_FORMS': [u''], u'name': [u'application1'], u'repository': [u'1'], u'application2server_set-INITIAL_FORMS': [u'2'], u'application2server_set-TOTAL_FORMS': [u'5'], u'csrfmiddlewaretoken': [u'bmEPdLCloNHKR0qUmwdhdmQ4aDKHV2CT'], u'servers': [u'1', u'2']}>"
Below are models and forms used:
class Server(models.Model):
host = models.CharField(max_length=100)
user = models.CharField(max_length=20)
root_directory = models.CharField(max_length=200)
class Repository(models.Model):
host = models.CharField(max_length=100)
user = models.CharField(max_length=20)
class Application(models.Model):
name = models.CharField(max_length=100)
repository = models.ForeignKey(Repository)
servers = models.ManyToManyField(Server, through='Application2Server', null=True, blank=True)
class Application2Server(models.Model):
application = models.ForeignKey(Application)
server = models.ForeignKey(Server)
tag = models.CharField(max_length=100)
deployment_date = models.DateTimeField(auto_now=True, blank=True)
class ServerForm(ModelForm):
class Meta:
model = Server
class RepositoryForm(ModelForm):
class Meta:
model = Repository
class ApplicationForm(ModelForm):
class Meta:
model = Application
class Application2ServerForm(ModelForm):
class Meta:
model = Application2Server
exclude = ('application', 'server', 'tag')
View used to save the date:
def app_edit(request, id):
application = get_object_or_404(Application, pk=id)
Application2ServerFormSet = inlineformset_factory(Application, Application2Server)
if request.method == 'POST':
form = ApplicationForm(request.POST, instance=application)
formset = Application2ServerFormSet(request.POST, instance=application)
if form.is_valid() and formset.is_valid():
saved_application = form.save()
formset.save()
return HttpResponseRedirect(reverse(
'proj.views.app_show',
args=(saved_application.pk,)
))
else:
form = ApplicationForm(instance=application)
formset = Application2ServerFormSet(instance=application)
return render(request, 'app_edit.html', {'form': form, 'formset': formset})
and template to generate the HTML and form itself:
<h1>App edit view</h1>
{% extends "base.html" %}
{% block container %}
<form method="post">{% csrf_token %}
<table>
{{ formset.management_form }}
{{ form.as_table }}
<tr><th></th><td><input type="submit" value="Save changes" /></td></tr>
</table>
</form>
{% endblock %}
Any help will be much appreciated! Thank you!
You need to render each form in the formset as well in the template.
Update your template to something like this (where you are rendering formset) :
<form method="post" action="">
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form.as_table }}
{% endfor %}
</table>
</form>
Reference: Formset in templates

Categories

Resources