I have a model form to display a textbox for the listname and a image for the user to upload. I am wanting the username to be pulled from the current request. When I currently hit submit, I am getting an "NOT NULL constraint failed: pages_newlist.user_id". I did a little experiment and when I include the field for the user to select their username from a list of all the users, it submits the data with no issues. Obviously that would be a huge flaw if I allowed the user to select which account to submit the data to, but my issue is that the form isn't auto populating the username field with the current request. My second issue is, when I do select admin as a user, the list posts fine, but the image url doesn't attach itself to the submission.
View
def control_panel(request, username):
context = RequestContext(request)
if username == request.user.username:
if request.user.is_authenticated():
user = User.objects.get(username=username)
lists = request.user.newlist_set.all()
listitems = request.user.newlistitem_set.all()
form = NewListForm(request.POST or None)
if form.is_valid():
save_it = form.save(commit = False)
save_it.save();
return render_to_response('controlpanel.html', {'form': form, 'lists': lists,'listitems':listitems,}, context)
else:
return render_to_response('login.html', {}, context)
else:
return render_to_response('controlpanel.html', {}, context)
Form
class NewListForm(forms.ModelForm):
class Meta:
fields = ('list_name', 'picture')
model = newlist
Fixing Image Not Posting
Needed to add enctype to template:
<form method = "POST" action = "" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
</form>
Populating User To Current User
Needed to just assign user for the entry to request.user
form = NewListForm(request.POST, request.FILES)
if form.is_valid():
save_it = form.save(commit = False)
save_it.user = request.user
save_it.save()
Related
I have a simple Django 3.1.0 app I need to create in order to assign Tasks with Tags (or assign tags into tasks).
Model
class Task(models.Model):
user = models.CharField(max_length=33)
time = models.DateTimeField(auto_now_add=True)
task = models.CharField(max_length=500)
tags = models.CharField(max_length=100, default="None", null=True)
class Tag(models.Model):
tag = models.CharField(max_length=30, default="No Tag")
members = models.ManyToManyField('Task', related_name="tag")
class Meta:
verbose_name = "tag"
verbose_name_plural = "tags"
view
def main(request):
model = Task.objects.values().all()
tags = Tag.objects.values().all()
form = TaskForm()
con = {'context': list(model), 'form': form, 'tags': list(tags)}
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = TaskForm()
return render(request, "tasks.html", con)
form
class TaskForm(ModelForm):
class Meta:
model = Task
fields = ['user', 'task', 'tags']
template_name = 'tasks.html'
tags = ModelMultipleChoiceField(
queryset= Tag.objects.all(),
widget=CheckboxSelectMultiple(), required=False,
)
task_form
<form method="post" class="form">
{% csrf_token %}
{{form}}
<input type="submit" value="Save">
</form>
This returns in the tags list the items listed as:
Tag object (1)
Tag object (2)
And when it saves when i press submit, it fetches in a table (in another template), the values saved in the text of <QuerySet [<Tag: Tag object (2)>]>
That's how it stores them in the database.
I have managed to extract the values as they are ('jenkins','AKS') and send them in the template using this (bootstrapvuejs) : {% for tag in tags %}<b-form-checkbox>{{tag.tag}}</b-form-checkbox>{% endfor %}, which lists them raw values perfectly.
However, when I do that modification, the form submitted is not written to database.
What am I missing?
UPDATE!
I have partly solved it by adding this into the Tag model:
def __str__(self):
return self.tag
but when it persists it on submit, it still saves it as:
<QuerySet [<Tag: jenkins>]>
So, how and where do I strip only the specific tag values to be inserted in the database?
Many Thanks
Alright so there is a couple issues with your code, first off your main view:
Change it from this:
def main(request):
model = Task.objects.values().all() # calling values without specifying an argument makes no sense so just call it like **Task.objects.all()**
tags = Tag.objects.values().all() # same here
form = TaskForm() # don't call your form here it gets reassigned later anyways
con = {'context': list(model), 'form': form, 'tags': list(tags)} # don't define your context here since you are reasigning your form later so the form instance is always TaskForm()
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = TaskForm()
return render(request, "tasks.html", con)
To this:
def main(request):
model = Task.objects.all()
tags = Tag.objects.all()
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
else:
form = TaskForm()
context = {'tasks': model,
'form': form,
'tags': tags}
return render(request, "tasks.html", con)
Then in your template pass your form with as_p method call:
{{ form.as_p }}
Hovewer the error you are getting is not because of your html or your view, it's because your tags field in your Task model is not a ManyToMany relationship to your Tag model but rather a simple CharacterField and you are trying to save objects to the CharField, so rewrite your Task model like this:
class Task(models.Model):
user = models.CharField(max_length=33)
time = models.DateTimeField(auto_now_add=True)
task = models.CharField(max_length=500)
tags = models.ManyToMany(Tags)
Then your form should save them in the tags field of your Task instance and you can view them like this:
task = Task.objects.get(pk=1)
task_tags = task.tags.all() # stores a queryset of all tags of the queried task
and in the template:
{% for tag in task.tags.all %}
...
{% endfor %}
OK , I solved the POST data that is saved in database as Queryset, by extracting in the view where save() is called, the field 'tags' likewise:
f = form.save(commit=False)
f.tags = request.POST['tags']
form.save()
The only problem now is that I have multiple checkboxes in the form but this way it extracts only one of them, whilst I would expect it to return a list like what is printed in the request.POST : <QueryDict: {'csrfmiddlewaretoken': ['XV7HgTFiWXEnrkhqT3IsqUN2JbnT7YIH5r6fKgh2ehqeLsLMpvCPdUU4N2qwWuPk'], 'user': ['afa'], 'task': ['aff'], 'tags': ['jenkins', 'AKS']}> -> from that I call 'tags' but it saves only 'jenkins' ...
UPDATE
OK, I RTFM and saw that there is a method on the QueryDict object that can be passed to request.POST.getlist('tags') , so now it returns the complete value of 'tags' key.
this might be a pretty stupid question. Also I am new to django. But I was trying to create a basic file upload approach with django where user uploads a file and it gets stored into the defined media path (or whatever that it's called) and that the file size, name, and some other attributes that are needed can be stored into the database. So I have the model ready which will help you understand the question better.
class Document(models.Model):
file_uid = models.CharField(max_length = 16)
file_name = models.CharField(max_length = 255)
file_size = models.CharField(max_length = 255)
file_document = models.FileField(upload_to='uploaded_files/')
uploaded_on = models.DateTimeField(auto_now_add=True)
uploaded_by = models.CharField(max_length=16)
Now it's clearly plain that we don't need to create all the fields in the form and that most them can be received from the file itself (like the name, size). for other attrs like uid and uploaded by those also will be added by the backend. So that's where I am stuck. I have searched for 2 days straight and still couldn't find a proper solution.
As of now this is my views.py
def uploadView(request):
if(request.method == 'POST'):
form = FileUploadForm(request.POST, request.FILES)
uploaded_file = request.FILES['uploaded_file']
file_dict = {
'file_uid' : get_random_string(length=10),
'file_name' :uploaded_file.name,
'file_size' : uploaded_file.size,
'file_document' : request.FILES['uploaded_file'],
'uploaded_by' : get_random_string(length=10)
}
form = FileUploadForm(data=file_dict)
if form.is_valid():
form.save()
return HttpResponse("You reached here")
else:
return HttpResponse("Your form is invalid")
else:
form = FileUploadForm(request.POST, request.FILES)
return render(request, 'function/upload.html', {
'form':form
})
I don't know if this is correct but as of know the form.isvalid() is false.
here's my forms.py
class FileUploadForm(forms.ModelForm):
file_document = forms.FileField(widget=forms.FileInput(attrs={'name':'uploaded_file'}))
class Meta:
model = Document
fields = ('file_uid', 'file_name', 'file_size', 'file_document', 'uploaded_by')
and my upload page section looks like this
<body>
<h1>Upload a file</h1>
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="uploaded_file">
<button type="submit">Upload</button>
</form>
</body>
If you can mercifully guide me into a proper way of doing this i'll be really gratefull.
Before solution, Here are few issues i found in your code
Major issue is how you tried to update the name of your file_document input, it doesn't work this way. confirm this by inspecting in devtools.
Checkout my answer here to update name attribute of django input form field.
Without updating this, you are not getting file from form.
Not issues just something i would like to point out
def uploadView(request):
if(request.method == 'POST'):
form = FileUploadForm(request.POST, request.FILES)
# your code in between, here the above form is never used and the overridden by the form in next line so why assigning it
form = FileUploadForm(data=file_dict)
# your form.is_valid() code start here
else:
form = FileUploadForm(request.POST, request.FILES)
# This block will only run for GET request, why using request.POST, request.FILES
return render(request, 'function/upload.html', {
'form':form
})
Here is how i got your code working
update FileUploadForm like this
class FileUploadForm(forms.ModelForm):
class Meta:
model = Document
fields = ('file_uid', 'file_name', 'file_size', 'file_document', 'uploaded_by')
# below code is only used to change the name of file_document to uploaded_file
custom_names = {'file_document': 'uploaded_file'}
def add_prefix(self, field_name):
field_name = self.custom_names.get(field_name, field_name)
return super(FileUploadForm, self).add_prefix(field_name)
use form in html like this
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{form.file_document}}
<input type="submit" value="send"/>
</form>
Update view as
def uploadView(request):
if(request.method == 'POST'):
uploaded_file = request.FILES['uploaded_file']
file_dict = {
'file_uid' : 'test1',
'file_name' :uploaded_file.name,
'file_size' : uploaded_file.size,
'uploaded_by' : 'hemant'
}
form = FileUploadForm(file_dict, request.FILES)
if form.is_valid():
form.save()
return HttpResponse("You reached here")
else:
return HttpResponse("Your form is invalid")
else:
form = FileUploadForm()
return render(request, 'function/upload.html', {
'form':form
})
I'm trying to get a photo to upload and the form is not seeing the file and in the form.errors, it says 'this field is required'. I've tried using picture = request.FILES['picture'] to no avail and have also tried picture = form.FILES['picture'] as well as picture = request.POST.FILES['picture'] and picture = form.cleaned_data.get('picture') What am I missing? Let me know if you need anymore information
template
{% block content %}
<h1>Create {{post_type.title}} Post</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button type='submit'>Submit</button>
</form>
{% endblock %}
forms.py
class PicturePostForm(forms.ModelForm):
class Meta:
model = PicturePost
fields = ('description', 'privacy', 'picture', 'categories')
views.py
#login_required()
def picture_post(request):
"""
Creates new picture post
"""
if request.method == "POST":
form = PicturePostForm(request.POST)
print("is post")
if form.is_valid():
print("is valid") # this never gets printed because of the 'this field is required' error
author = request.user
content = form.cleaned_data['description']
category = form.cleaned_data['categories']
picture = form.cleaned_data['picture']
privacy = form.cleaned_data['privacy']
p_post = PicturePost(author=author, description=content, categories=category, picture=picture,privacy=privacy )
p_post.save()
#redirect to last page
return redirect('home')
else:
l = []
for i in form.errors.keys():
l.append(form.errors[i])
return HttpResponse(l)
else:
post_type = 'picture'
form = PicturePostForm()
return render(request, 'create_post.html', {'form': form, 'post_type': post_type})
The corresponding model field
picture = models.ImageField(upload_to=f'profiles/{User}_gallery', max_length=255)
Fixed it by replacing form = PicturePostForm(request.POST) with form = PicturePostForm(request.POST, request.FILES)
I have tried to complete the code before, please following
views
#login_required()
def picture_post(request):
"""
Creates new picture post
"""
form = PicturePostForm(request.POST or None, request.FILES or None)
if request.method == "POST":
if form.is_valid():
# instance new object p_post (this best practice if using forms.ModelForm)
# commit=False (to save data on ram/memory device without database/hardrive)
p_post = form.save(commit=False)
# assign author attribute from thr current user session
p_post.author = request.user
# commit=True to move/save data from memory to harddrive
p_post.save() # p_post.save(commit=True)
return redirect('home')
else:
l = []
for i in form.errors.keys():
l.append(form.errors[i])
return HttpResponse(l)
post_type = 'picture'
return render(request, 'create_post.html', {'form': form, 'post_type': post_type})
I am unable to see pre-existing form data when updating. The forms work fine, after submitting the database is updated, but in order to submit the user must enter all form data (including data that will not be updated). While reentering, the previous data is not visible. Is there a way to display the current data of the model instance being updated in the form fields?
Forms:
UpdateSomethingForm(forms.ModelForm):
class Meta:
model = Something
fields = ['field1', 'field2', 'field3']
Views:
def update_something(request, object_pk):
form = UpdateSomethingForm()
context_dict = {}
try:
instance = Something.objects.get(pk=object_pk)
context_dict['instance'] = instance
except Something.DoesNotExist:
context_dict['instance'] = None
if request.method == 'POST':
form = UpdateSomethingForm(request.POST, instance=instance)
if form.is_valid():
form.save(commit=True)
return HttpResponseRedirect('/home')
else:
print(form.errors)
context_dict['form'] = form
return render(request, 'form.html', context=context_dict)
Html:
<form role="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
You passed the instance argument on POST, but not on GET.
form = UpdateSomethingForm(instance=instance)
in full:
def update_something(request, object_pk):
try:
instance = Something.objects.get(pk=object_pk)
except Something.DoesNotExist:
instance = None
if request.method == 'POST':
form = UpdateSomethingForm(request.POST, instance=instance)
if form.is_valid():
form.save()
return HttpResponseRedirect('/home')
else:
form = UpdateSomethingForm(instance=instance)
context_dict = {'form': form, 'instance': instance}
return render(request, 'form.html', context_dict)
The main problem is that you construct an empty Form, even if the instance can be found. But you make the view rather "chaotic" in the first place.
Probably a more readable view is:
def update_something(request, object_pk):
context_dict = {}
try:
instance = Something.objects.get(pk=object_pk)
except Something.DoesNotExist:
instance = None
context_dict['instance'] = instance
if request.method == 'POST':
form = UpdateSomethingForm(request.POST, instance=instance)
if form.is_valid():
form.save(commit=True)
return redirect('view_name')
else:
form = UpdateSomethingForm(instance=instance)
context_dict['form'] = form
return render(request, 'form.html', context=context_dict)
Here we ensure that the instance variable is always defined, also in the case the except body is "firing".
Furthermore it is probably better to use a redirect(..) and pass the name of the view over an URL, since if you change the URL of that view, this will still work.
I am new to django and trying to show a form in an html file and I don't see the fields when I get to this particular page on my browser. Anybody has an idea why?
Here is the html file : In which I can see everything but the form showing up
add_device.html
{% extends 'layout/layout1.html' %}
{% block content %}
<form action = "userprofile/" method = "post">
{% csrf_token %}
{{ form }}
<input type = "submit" value = "Submit"/>
</form>
{% endblock %}
forms.py
from django import forms
from models import UserProfile
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('deviceNb',)
models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
deviceNb = models.CharField(max_length = 100)
User.profile = property(lambda u : UserProfile.objects.get_or_create(user = u)[0])
views.py
def user_profile(request):
if request.method == 'POST':
#we want to populate the form with the original instance of the profile model and insert POST info on top of it
form = UserProfileForm(request.POST, instance=request.user.profile)
if form.is_valid:
form.save()
#to go back to check that the info has changed
return HttpResponseRedirect('/accounts/loggedin')
else:
#this is the preferred way to get a users info, it is stored that way
user = request.user
profile = user.profile
#if we have a user that has already selected info, it will pass in this info
form = UserProfileForm(instance=profile)
args = {}
args.update(csrf(request))
args['form'] = form
print(form)
return render_to_response('profile.html',args)
I am pretty sure my url file is ok, since I get to the right urls, my problem is really the form fields not showing up.
Thank you so much!!
You are not handling GET request in your view. Update code of the view as
def user_profile(request):
if request.method == 'POST':
# your existing code
# .....
else : #when its get request
form = UserProfileForm(instance=request.user.profile)
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('profile.html',args)
This is a sample code, it can be improved.
The indentation of your view is incorrect. The else block belongs with the if request.method == 'POST' statement, and handles GET requests.
You also need to fix the indentation at the end of the method, so that you return a response for get and post requests. It's better to use render instead of the obsolete render_to_response. This simplifies your code, because you don't need to call args.update(csrf(request)) anymore.
from django.shortcuts import render
def user_profile(request):
if request.method == 'POST':
#we want to populate the form with the original instance of the profile model and insert POST info on top of it
form = UserProfileForm(request.POST, instance=request.user.profile)
if form.is_valid:
form.save()
#to go back to check that the info has changed
return HttpResponseRedirect('/accounts/loggedin')
else:
#this is the preferred way to get a users info, it is stored that way
user = request.user
profile = user.profile
#if we have a user that has already selected info, it will pass in this info
form = UserProfileForm(instance=profile)
args = {}
args['form'] = form
return render(request, 'profile.html', args)
You should handle GET request, too. Try this in your view:
def user_profile(request):
form = UserProfileForm()
if request.method == 'GET':
# handle GET request here
form = UserProfileForm(instance=request.user.profile)
elif request.method == 'POST':
#we want to populate the form with the original instance of the profile model and insert POST info on top of it
form = UserProfileForm(request.POST, instance=request.user.profile)
if form.is_valid:
form.save()
#to go back to check that the info has changed
return HttpResponseRedirect('/accounts/loggedin')
args = {}
args['form'] = form
return render_to_response('profile.html',args)
And in your profile.html, you can do something like this:
{{ form.as_p }}