Django - save multiple input parameters to database - django

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})

Related

tags will not store in database in django

Tags will not store in database in Django
def addque(request):
if request.method == "POST":
user = request.user
if user.is_anonymous:
return redirect('addquery')
if user.is_active:
question = request.POST['question']
body = request.POST['body']
tags = request.POST['tags']
aquuid = request.user.aquuid
addquery = Question(question=question, user_id=aquuid, que_body=body, tags=tags)
addquery.save()
return redirect('addquery')
else:
return render(request, 'question/ask.html')
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. What is the issue with the code?
Tags are Many-to-Many objects and you can't add those to an object until the object has been saved. The documentation shows that you need to use .add() to add tags to a model instance. Your code should be:
addquery = Question(question=question, user_id=aquuid, que_body=body)
addquery.save()
addquery.tags.add(tags)
As an aside, you might be better served by a ModelForm which can handle the tags and all of this stuff that you're doing:
question = request.POST['question']
body = request.POST['body']
tags = request.POST['tags']
https://django-taggit.readthedocs.io/en/latest/forms.html
Use model form
In html use id of forms like this
HTML
<form action="{% url 'addquery' %}" method="post">
{% csrf_token %}
<div class="psrelative">
<input id="id_question" name="question" type="text" maxlength="300" tabindex="100" placeholder="e.g. Is there an R function for finding the index of an element in a vector?" class="s-input js-post-title-field" value="" data-min-length="15" data-max-length="150" autocomplete="off" required>
</div>
<textarea name="que_body" id="id_que_body" class="textarea-body"></textarea required>
<div class="psrelative">
<input id="id_tags" name="tags" type="text" maxlength="300" tabindex="100"
placeholder="e.g. (ruby-on-rails vba spring)" class="s-input js-post-title-field" value="" data-min-length="15" data-max-length="150" autocomplete="off" required>
</div>
<button class="review-question-btn" type="submit" tabindex="120"> Submit your question
</button>
</form>
Forms.py
from django import forms
from .models import Question
class Addqueform(forms.ModelForm):
class Meta:
model = Question
fields = ['question','que_body','tags']
Views.py
from .forms import Addqueform
def addque(request):
queform = Addqueform(request.POST)
if request.method == "POST":
user = request.user
if user.is_anonymous:
return redirect('addquery')
if user.is_active:
if queform.is_valid():
aquuid = request.user.aquuid
question = queform.cleaned_data['question']
body = queform.cleaned_data['que_body']
tags = queform.cleaned_data['tags']
addquery = Question(question=question, user_id=aquuid, que_body=body)
for tag in tags:
addquery.tags.add(tag)
addquery.save()
return redirect('addquery')
else:
queform = Addqueform()
return render(request, 'question/ask.html', {'form': queform})
else:
return render(request, 'question/ask.html', {'form': queform})
I think It will Work

CRUD not able to displaly model instances in template to update

I have an app project. I am able to save instances to the model in the database no problem. But I cannot pull in the instance by pk to the html form to edit and update. please see setup below can anyone provide any guidence or help, as to why this is not happening and how I can resolve?
views.py
def edit_properties(request, id):
properties = Properties.objects.get(pk=id)
context = {
'properties': properties,
'values': properties,
}
if request.method == 'GET':
return render(request, 'sub/edit_properties.html', context)
you better use ModelForm to update data in your models
class PropertiesEditForm(forms.ModelForm):
class Meta:
model = Properties
then you need to update your view to use this form
def edit_properties(request, id=None):
properties = Properties.objects.get(pk=id) if id else None
form = PropertiesEditForm(request.POST or None, instance=properties)
if request.POST:
if form.is_valid():
form.save()
return HttpResponseRedirect("url_to_show_your_new_data")
context = {
'form': form,
}
return render(request, 'sub/edit_properties.html', context)
and in html file you use this form
<form method="POST" action="URL_TO_EDIT_PROPERTIES_VIEW">{% csrf_token %}
{{ form }}
<input type="submit"/>
</form>
Found the issue, in my html the "{{values.address1}}" should be "{{values.Address1}}" as the attribute in the database is capital first letter.
Little less stressed now :)
<div class="form-row my-3">
<div class="form-group col-md-6">
<label for="Addressline1">Address line 1</label>
<input type="text" class="form-control" name="address1" id="address1Field"
value="{{values.address1}}">
</div>
Thank you all for your help,

Django file upload not displaying

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

Django model form for user profile won't pre-populate

I've made a profile model in models.py:
class Profile(models.Model):
user = models.OneToOneField(User)
title = models.CharField(max_length=20, default='title')
firstname = models.CharField(max_length=40, default='firstname')
lastname = models.CharField(max_length=40, default='lastname')
blurb = models.CharField(max_length=500, default='tell us about yourself')
#work out how to make filename equal the username
pic = models.ImageField(default="static/profile/blank.png", upload_to='static/profile/%d%m%y.jpg') #+ user.pk + '.jpg')
def __unicode__(self):
return self.user.username
and here is the view for a page to edit the profile of a logged in user:
def editprofile(request):
u_p = request.user.profile
template = loader.get_template('registration/editprofile.html')
if request.method == 'POST':
form = ProfileForm(request.POST, instance=u_p)
if form.is_valid():
form.save()
else:
# todo
None
else:
#todo
context = RequestContext(request, {'form': form})
return HttpResponse(template.render(context))
The template fragment reads:
<form method="POST" action=".">
{% csrf_token %}
<div class="regformout">
<div class="regform">
{% for field in form %}
<div class='cell'> {{ field.label_tag }} </div>
<div class='nogin'> {{ field.errors }} </div>
<div class='cell'> {{ field }} </div>
{% endfor %}
</div>
</div>
<input class="btn btn-large btn-primary" type="submit" value="Save Your Profile" ></input>
</form>
I want the form fields to automatically populate with the data for the current user on the corresponding page for editing the profile. However, no matter what I try I cannot make this happen. What am I doing wrong here?
Your main problem is you are only populating the form if the user hits the submit button, so when the view is requested initially, your form is empty.
from django.shortcuts import render, redirect
def editprofile(request):
u_p = request.user.profile
form = ProfileForm(request.POST or None, instance=u_p)
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect('/')
return render(request,
'registration/editprofile.html', {'form': form})

how to submit a form and formset at the same time

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.