I have written the code for file upload but it is not displaying on my destination page.
Please help me edit my code or suggest how to fix this issue.
The rest of the fields are displaying but not the file field
My models.py
class Help(models.Model):
researcher = models.CharField(max_length=100)
study = models.CharField(max_length=500)
date = models.DateTimeField(auto_now_add=True)
document = models.FileField(upload_to='documents/', null=True, blank=True)
forms.py
from django import forms
from .models import Help
from django.forms import ModelForm
class AboutHelp(forms.ModelForm):
class Meta:
model = Help
fields = '__all__'
source page
<form action="{% url 'lazer.views.about_experiment' exp.link_name %}" method="POST" name="form">
{% csrf_token %}
<label>Researcher Name(s):
<input type="text" name="researcher"><br>
<lable>Study Summary
<textarea rows="10" cols="50" placeholder="Start typing..." maxlength="500" class="form-control" name="study"></textarea>
<br>
<label>Upload your IRB approval letter:
<input type ="file" id="irb-file" class="file_input" name="document"></label>
<br>
<input type = "submit" value="Submit" class="btn btn-primary" />
</form>
views.py
def about_experiment(request, ex_link_name):
researcher = None
study = None
posts = None
exp = get_object_or_404(Experiment,link_name = ex_link_name)
high_scores = ScoreItem.objects.filter(experiment=exp,active=True)
context = {
'request': request,
'exp':exp,
'high_scores': high_scores,
'awards':AwardItem.objects.filter(experiment=exp,visible=True),
}
if exp.about_file:
context['about_file'] = settings.EXPERIMENT_DIRS+exp.about_file.get_include_path()
return render(request, 'about_experiment.html', context)
if request.method == 'POST':
form = AboutHelp(request.POST, request.FILES)
posts = Help.objects.filter().order_by('-date')[0]
if form.is_valid():
obj = form.save(commit = False)
obj.save()
researcher = form.cleaned_data['researcher']
study = form.cleaned_data['study']
document = form.cleaned_data['document']
else:
form = AboutHelp()
posts = Help.objects.filter().order_by('-date')[0]
return render(request, 'about_experiment.html', {'posts': posts})
return render(request, 'about_experiment.html', {'posts': posts})
destination page
<h4><b>{{ posts.researcher }}</b></h4>
<p>{{ posts.study }}</p>
<p>Uploaded file is : {{ posts.document }}</p>
Have you checked your file is been saved? And I think you have not understood the use of Django forms yet.Here's how to .You're creating the form but you're displaying fields manually and in view, you getting the data by forms.cleaned_data. Also, to save files you need to define <form enctype="multipart/form-data" > Here's why
Related
I have some problem with the form and Django.
I would like to do a custom (in column and with style) form from a model. But after few hours to try I thinks it's not possible.
So I decide to create not a forms.ModelForm but a forms.Form, in order to custom my form. But in this way i can't keep the validations...
And last thing I try, it in the template file to write a validation manually, it's work but when I submit my form with error, and when it's refresh, all the data are loose, and I see just the error message...
So it make me crazy... I just would like to do a nice form with django...
Thanks in advance
forms.py
class PatienceForm(forms.ModelForm):
class Meta:
model = Patience
fields = ('nom', 'prenom', 'age', 'email')
template.html
<form method="POST" accept-charset="utf-8">
{% csrf_token %}
<p><label for="id_nom">Nom :</label> <input type="text" name="nom" maxlength="200" required id="id_nom"></p>
<p><label for="id_prenom">Prenom :</label> <input type="text" name="prenom" maxlength="200" required id="id_prenom"></p>
<p><label for="id_age">Age :</label> <input type="number" name="age" required id="id_age"></p>
<p><label for="id_email">Email :</label> <input type="email" name="email" maxlength="254" required id="id_email"></p>
{% for err in form.email.errors %}
{{err}}
{% endfor %}
<button type="submit" class="save btn btn-default">Save</button>
view.py
def post_essaie(request):
if request.method == "POST":
form = PatienceForm(request.POST)
if form.is_valid():
logging.info('Hello')
post = form.save(commit=False)
post.published_date = timezone.now()
post.save()
return render(request, 'blog/succes.html')
return render(request, 'blog/teste.html', {'form': form})
else:
form = PatienceForm()
return render(request, 'blog/teste.html', {'form': form})
Thanks in advance for your help
for your form.py :
class PatienceForm(forms.Form):
nom = forms.CharField(max_length=200,label='Nom')
prenom = forms.CharField(max_length=200)
age = forms.integerField(max_length=200)
email = forms.EmailField(max_length=254)
# use clean to verify your form
def clean(self):
cleaned_data = super(ContactForm, self).clean()
nom = cleaned_data.get('nom')
prenom = cleaned_data.get('prenom')
age = cleaned_data.get('age')
email = cleaned_data.get('email')
if not name and not email and not message:
raise forms.ValidationError('le formulaire est vide')
in your views.py you can use :
from django.shortcuts import render
from .forms import ContactForm
def post_essaie(request):
if request.method == 'POST':
form = PatienceForm(request.POST)
if form.is_valid():
logging.info('Hello')
nom = form.cleaned_data['nom']
prenom = form.cleaned_data['prenom']
age = form.cleaned_data['age']
email = form.cleaned_data['email']
# here create your entry with all the data from your form then add
# your published_date
post = Patience(nom=nom, prenom=prenom, age=age,
email=email, published_date = timezone.now())
post.save()
return render(request, 'blog/succes.html')
else:
form = PatienceForm()
return render(request, 'blog/teste.html', {'form': form})
Voila Bonne journée !!
I want to user have a avatar. So I created the model and the form.
Image is not displaying on the page. But form saves the image to folder. Where is mistake?
request.user.avatar.url doesn't work. Maybe the image is not attached to User? Thanks for the help
P.S. djagno-avatar is not good for me.
models.py
class Avatar(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, null=True)
avatar = models.ImageField(upload_to='user_avatars', null=True)
forms.py
class UserPhotoForm(forms.ModelForm):
class Meta:
model = Avatar
fields = ('avatar', )
widgets = {
'avatar': forms.FileInput(attrs={'class': 'input'}),
}
views.py
def cabinet(request):
user = request.user
if request.method == 'POST':
form = UserPhotoForm(request.POST, request.FILES, instance=user)
if form.is_valid():
form.save()
else:
form = UserPhotoForm()
return render(request, 'account/cabinet/cabinet.html', {'form': form})
cabinet.html
<div class="avatar">
{{ request.user.avatar.url }} #there is trying
{{ request.user.avatar.avatar.url }}
<img src="{{ request.user.avatar.avatar.url }}" alt="" width="80px" height="80px">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" id="file"/>
</form>
try this
<img src="{{ request.user.avatar.url }}" alt="" width="80px" height="80px">
https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ImageField
Found the main problem you are never storing your avatar model in db. As you are giving it the instance of your user. Because of that your Avatar model is not stored.
You have to use
def cabinet(request):
user = request.user
if request.method == 'POST':
form = UserPhotoForm(request.POST,request.FILES)
if form.is_valid():
temp=form.save(commit=False)
temp.user = user
temp.save()
else:
form = UserPhotoForm()
return render(request, 'account/cabinet/cabinet.html', {'form': form})
Try that
I'm using the below custom form in my Django app:
forms.py
class UpdateURLForm(forms.ModelForm):
VideoURL = forms.URLField()
MainDescription = forms.CharField(max_length=100)
class Meta:
model = Listing
fields = ('VideoURL', 'MainDescription',)
Then, in views.py I import the form and then I render the fields into my HTML template:
def edit_info(request):
if request.method == 'POST':
form = UpdateURLForm(request.POST)
if form.is_valid():
VideoURL = form.cleaned_data['VideoURL']
MainDescription = form.cleaned_data['MainDescription']
else:
form = UpdateURLForm()
return render(request, 'myapp/updateinfo.html', {'form': form})
HTML:
<form action="#" method='post' class="card shadow-soft border p-4 mb-4">{% csrf_token %}
<div class="form-group">
<label for="video">Video URL:</label>
{{form.VideoURL|add_class:"form-control shadow-soft"}}
</div>
<div class="form-group">
<label for="video">Video URL:</label>
{{form.MainDescription|add_class:"form-control shadow-soft"}}
</div>
<div class="row">
<div class="col">
<button class="btn btn-primary btn-dark mt-2 animate-up-2 text-right"
type="submit">Update</button>
</div>
</div>
</form>
Now, my question is: how can I render the field MainDescription in the template in order to save both information into the database? The way I rendered the second field (MainDescription) doesn't work. Thanks!
Edit
So, I have two fields in my custom form which (VideoURL and MainDescription) which I would like to use to update some info in the DB. When I try to render in the HTML template both are getting the same ID whereas I was expecting that each field of the form to be rendered:
<input type="text" name="VideoURL" value="https://videourl.com" maxlength="100" class="form-control shadow-soft" required="" id="id_VideoURL">
I do not figure out what I am missing.
Every ModelForm has a save() method. This method creates and saves a database object from the data bound to the form. A subclass of ModelForm can accept an existing model instance as the keyword argument instance; if this is supplied, save() will update that instance. If it’s not supplied, save() will create a new instance of the specified model:
EXAMPLE 1 with forms.ModelForm
forms.py
class UpdateURLForm(forms.ModelForm):
class Meta:
model = Listing
fields = ['video_url', 'main_description']
So you can ease save your form.
views.py
def edit_info(request):
if request.method == 'POST':
form = UpdateURLForm(request.POST)
if form.is_valid():
instance = form.save()
else:
form = UpdateURLForm()
return render(request, 'myapp/updateinfo.html', {'form': form})
EXAMPLE 2 with forms.Form
forms.py
class UpdateURLForm(forms.Form):
video_url = forms.URLField(label="Video Url")
main_description = forms.CharField(label="Description", max_length=100)
You can also import your model and create an object.
views.py
from models import Listing
def edit_info(request):
if request.method == 'POST':
form = UpdateURLForm(request.POST)
if form.is_valid():
listing = Listing(video_url=form.cleaned_data.get('video_url'), main_description=form.cleaned_data.get('main_description'))
listing.save()
else:
form = UpdateURLForm()
return render(request, 'myapp/updateinfo.html', {'form': form})
In short:
I have created a Post model and Comment model and created a comment form, I am serving a single url which will show all posts, related comment and a comment form to enter new comments. With a submission page is reloaded with new comments. But when I submit the comment I get the error:
django.db.utils.IntegrityError: NOT NULL constraint failed: book_comment.related_post_id
This is one answer that looked promising but I am unable to do something.
I think it is not getting parent post id.
Long Version:
This is my model File:
def user_image_path(instance, filename):
return f"profile/user_{random.randint(1,1000)}_{filename}"
class Post(models.Model):
post_title = models.CharField(max_length=250)
post_creator = models.CharField(max_length=150)
creator_pic = models.ImageField(upload_to=user_image_path)
post_body = models.TextField()
post_created = models.DateTimeField(auto_now_add=True)
post_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.post_title} **{self.post_creator}**"
class Comment(models.Model):
related_post = models.ForeignKey(Post, related_name="comments")
comment_creator = models.CharField(max_length=150)
comment_body = models.CharField(max_length=1024)
comment_created = models.DateTimeField(auto_now_add=True)
comment_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.comment_creator}"
This is my form:
from django import forms
from .models import Post, Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['comment_creator', 'comment_body']
This is views:
from django.shortcuts import render, HttpResponseRedirect,reverse
from .models import Comment, Post
from .forms import CommentForm
# Create your views here.
def servePage(request):
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form.save()
HttpResponseRedirect(reverse('serve'))
else:
form = CommentForm()
posts = Post.objects.all()
return render(request, 'book/showpost.html', {'posts': posts, 'form': form})
This is my html template:
{% extends 'book/base.html' %}
{% block content %}
<h1>Welcome to book of life</h1>
<h2>New posts</h2>
<ul>
{% for post in posts %}
<li>{{ post.post_title }} by <small>{{ post.post_creator }}</small></li>
<p>{{ post.post_created|timesince }}</p>
<p>Content: <span>{{ post.post_body }}</span></p>
{# <br>#}
<h3>Comments:</h3>
{% for comment in post.comments.all %}
<p>{{ comment.comment_creator }} => {{ comment.comment_body }}</p>
{% endfor %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" name="comment">
</form>
<br><br>
{% endfor %}
</ul>
{% endblock %}
Sorry for any mistakes. Thanks in advance.
I believe this error has occurred because you have tried to create a new record in the comment model that leaves the related_post field blank, when it shouldn't be. If you are happy for this field to be left blank, you can change the field to be the following in models.py :
related_post = models.ForeignKey(Post, related_name="comments", null=True)
Alternatively, you may not want this to be blank. If you add related_post to the fields in the form, a drop down box will be created with all the posts and you can select one of these to comment on.
You may also be able to automatically detect what post you are commenting on, but I'm unsure how this is done.
Thanks to #cbuch1800 I finally got the answer. Here it is and the changes to file:
In template file after {{form.as_p}} I added a line to pass the current post Primary Key(id) to view.
Template file:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="hidden" name="post_pk" value={{ post.pk }}>
<input type="submit" name="comment">
</form>
In view file retrieved the post object and added the post object to related comment.
View:
def servePage(request):
if request.method == 'POST':
form = CommentForm(request.POST)
post = Post.objects.get(pk=request.POST['post_pk'])
if form.is_valid():
comment = form.save(commit=False)
comment.related_post = post
## related_post from Comment model
comment.save()
HttpResponseRedirect(reverse('serve'))
else:
form = CommentForm()
posts = Post.objects.all()
return render(request, 'book/showpost.html', {'posts': posts, 'form': form})
I am trying to render a form and a formset at once.
The formset is working fine (i think), but the form is not validating (as if there was no data being posted)
i have tried playing with the button but its main submit function comes through js.
the forms all work independently but not when submitted togetehr so it seem like the problem is in the views
here is the code:
views.py
from django.shortcuts import render, render_to_response
from django.http import HttpResponseRedirect
from forms import LessonForm, AddMaterialForm
from models import Lesson, SUBJECT_OPTIONS, Materials, MATERIAL_TYPES
from django.forms.formsets import formset_factory
def Create_Lesson(request):
AddMaterials=formset_factory(AddMaterialForm, extra=9)
if request.method == "POST": # If the form has been submitted...
lesson = LessonForm(request.POST, prefix="lesson") # A form bound to the POST data
formset = AddMaterials(request.POST, request.FILES) # A form bound to the POST data
if lesson.is_valid() and formset.is_valid(): # All validation rules pass
lesson = lesson.save(commit=False)
lesson.creator = request.user
lesson.save()
for form in formset:
form = form.save(commit=False)
form.lesson = lesson.pk
form.save()
return render(request, 'index.html',)
else:
lesson= LessonForm(prefix='lesson') # An unbound form
formset = AddMaterials()
return render(request, 'create_lesson/create.html', {
'form': lesson,'formset':formset
})
.html
<form id="create_lesson_form" method="post" action="">
<h2>1: Create Your Lesson</h2>
{{ form.non_field_errors }}
<label for="subject"><span>Subject</span></label>
{{form.subject}}
{{ form.subject.errors }}
<label for="title"><span>Title</span></label>
<input type="text" id="title" name="name" placeholder="Give it a name"/>
{{ form.name.errors }}
<label class="error" for="title" id="title_error">You must choose a title!</label>
<label for="subtitle"><span>Subtitle</span></label>
<input type="text" id="subtitle" name="subtitle" placeholder="Type your subtitle here"/>
{{ form.subtitle.errors }}
<label class="error" for="subtitle" id="subtitle_error">are you sure you want to leave subtititle blank?</label>
<label for="description"><span>Description</span></label>
<textarea id="description" name= "description" cols="42" rows="5" placeholder="why is it important? this can be a longer description"'></textarea>
{{ form.description.errors }}
<label class="error" for="description" id="description_error">are you sure you want to leave the description blank?</label>
<label for="success" id="Goals_title"><span>Goals</span></label>
<textarea id="success" name="success" cols="42" rows="5" placeholder="explain what sucess might look like for someone doing this lesson...what does mastery look like?" '></textarea>
{{ form.success.errors }}
<label class="error" for="success" id="success_error">are you sure you want to leave the goals blank?</label>
{{ form.directions.errors }}
<label class="error" for="directions" id="directions_error">are you sure you do not want to include dierections?</label>
<label for="directions" id="Directions_title"><span>Directions</span></label>
<textarea id="directions" name="directions" cols="42" rows="5" placeholder="you can add simple directions here" '></textarea><br>
</form>
<form id="add_elements_form" method="post" action="">
{% csrf_token %}
{{ formset.as_p}}
<button type='submit' id='finish'>Finish Editing Lesson</button>
</form>
This will submit the form and the formset at the same time.
//When your uploading files or images don't forget to put "multipart/form-data"
// in your form.
//To connect formset in your form, don't forget to put the model in the formset
// for instance.
//In this you can add many lines as you want or delete it.
forms.py
class LessonForm(forms.ModelForm):
class Meta:
model = Lesson
MaterialsFormset = inlineformset_factory(Lesson, Materials,
fields=('field_name', 'field_name'), can_delete=True)
views.py
def view_name(request):
form = LessonForm()
formset = MaterialsFormset(instance=Lesson())
if request.method == 'POST':
form = LessonForm(request.POST)
if form.is_valid():
lesson = form.save()
formset = MaterialsFormset(request.POST, request.FILES,
instance=lesson)
if formset.is_valid():
formset.save()
return render(request, 'index.html',)
return render(request, "page.html", {
'form': form, 'formset': formset
})
html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ formset.as_p }}
<input type="submit" value="Save"/>
</form>
You only need one form tag. If you expect to receive all of the data at the same time, you need to wrap all of the fields with one form tag.
Now that django 4 is out it's possible to do the same thing within the form itself using the Reusable templates. I prefer this solution because it is then simpler to reuse complex forms without messing with the views.
For the record, here is how I do it
The 2 related models:
# models.py
from django.db import models
class Recipe(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Ingredient(models.Model):
name = models.CharField(max_length=100)
quantity = models.FloatField()
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name="ingredients")
def __str__(self):
return self.name
The forms bound together:
# forms.py
from django import forms
from .models import Recipe, Ingredient
class IngredientForm(forms.ModelForm):
class Meta:
model = Ingredient
exclude = ('recipe',)
IngredientFormSet = forms.inlineformset_factory(Recipe, Ingredient, form=IngredientForm)
class RecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = '__all__'
template_name = 'recipe_form.html'
def __init__(self, *args, **kwargs):
"""Initialize the formset with its fields."""
self.formset = IngredientFormSet(*args, **kwargs)
super().__init__(*args, **kwargs)
def get_context(self):
"""Add the formset to the context for rendering."""
context = super().get_context()
context['formset'] = self.formset
return context
def save(self, commit=True):
"""Bind both models together."""
instance = super().save(commit=False)
self.formset.instance = instance
if self.formset.is_valid():
instance.save()
self.formset.save()
return instance
The template for the form:
<!-- templates/recipe_form.html -->
<p>Recipe: {{ form.name }}</p> <!-- calling "form" creates a rendering recursion -->
<p>Ingredients:</p>
{{ formset.management_form }}
<ul>
{% for elt in formset %}
<li>{{ elt }}</li>
{% endfor %}
</ul>
And the view using it:
# views.py
from django.views.generic import TemplateView
from .forms import RecipeForm
class RecipeView(TemplateView):
template_name = 'recipe.html'
def get_context_data(self):
context = super().get_context_data()
context['form'] = RecipeForm()
return context
def post(self, *args, **kwargs):
form = RecipeForm(self.request.POST)
if form.is_valid():
form.save()
else:
raise Exception('Something bad happened!')
return self.get(*args, **kwargs)
With a very basic template:
<!-- templates/recipe.html -->
<form action="." method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Submit</button>
</form>
And finally you're good to go:
# tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Recipe
class TestFormSet(TestCase):
def test_new_recipe(self):
data = {
"name": "quiche",
"ingredients-TOTAL_FORMS": 3,
"ingredients-INITIAL_FORMS": 0,
"ingredients-0-name": 'bacon bits',
"ingredients-0-quantity": 200,
"ingredients-1-name": 'eggs',
"ingredients-1-quantity": 4,
"ingredients-2-name": 'cream',
"ingredients-2-quantity": 150,
}
r = self.client.post(reverse('recipe'), data=data)
self.assertEqual(Recipe.objects.first().ingredients.count(),3)
$ python manage.py test
OK
Hopefully it will be useful to somebody.