views.py
#login_required(login_url='login/')
def add_country(request):
if request.method == 'POST':
form = CountryForm(request.POST,request.FILES)
if form.is_valid():
new_form = form.save(commit=False)
new_form.edited_by = request.user
new_form.save()
return redirect('country_details')
else:
form = CountryForm()
context = {'form':form}
return render(request,'add_country.html',context)
models.py
class Countries(models.Model):
CONTINENTS = [
('Asia','Asia'),
('Europe','Europe'),
('Africa','Africa'),
('Oceania','Oceania'),
('North America','North America'),
('South America','South America'),
]
name = models.CharField(max_length=75)
continent = models.CharField(max_length=50,choices=CONTINENTS,null=True)
landmark = models.CharField(max_length=100,null=True)
food = models.CharField(max_length=100,null=True)
entertainment = models.CharField(max_length=100,null=True)
flag = models.FileField(upload_to='flags', default='default.png',null=True)
image = models.FileField(upload_to='travel', default='default.png',null=True)
edited_by = models.OneToOneField(User,on_delete=models.CASCADE,null=True)
last_updated = models.DateTimeField(auto_now_add=True,null=True)
def __str__(self):
return self.name
add_country.html
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{ form.name.label }}<br>
{{ form.name }}<br><br>
{{ form.landmark.label }}<br>
{{ form.landmark }}<br><br>
{{ form.food.label }}<br>
{{ form.food }}<br><br>
{{ form.entertainment.label }}<br>
{{ form.entertainment }}<br><br>
{{ form.flag.label }}<br>
{{ form.flag }}<br><br>
{{ form.image.label }}<br>
{{ form.image }}<br><br>
<input type="submit" class="btn btn-primary" value="Add">
</form>
I have an issue that after I added the edited_by to assign the currently logged in user into that column then the form could not be submitted and only stayed on the same page instead of redirecting to the page that I want. I have tried different ways to make the form being submitted such as put request.method == "POST" and the page didn't work. However before I added edited_by into the models the form could be submitted accordingly and the data is being updated. May I ask what is the method to assign the user into the column edited_by after that user has added a post?
I think you have included edited_by field in your forms too.
If you are handling this field by yourself in the views then remove this field from your forms.
class CountryForm(forms.ModelForm):
class Meta:
model = Country
exclude = ['edited_by'] # or specify only required fields in form
Now your view will work fine.
Note: You can display your form's errors with {{form.errors}} in your template.
Related
Picture gets uploaded on the django admin panel but when i click on the image on the panel it shows the page not found error.
forms.py
class ApproveImgForm(forms.ModelForm):
class Meta:
model = ApprovImg
fields = ['photo']
urls.py
path('w_p.html', views.WProduct_list, name='WProduct_list'),
views.py
def WProduct_list(request, category_slug=None):
category = None
categories = Category.objects.all()
wproducts = Product.objects.filter()
if category_slug:
category = get_object_or_404(Category, slug=category_slug)
wproducts = Product.objects.filter()
if(request.method=='POST'):
form = ApproveImgForm(request.POST, request.FILES)
form.save()
context = {
'category': category,
'categories': categories,
'wproducts': wproducts,
}
return render(request, 'shop/w_p.html', context)
models.py
class ApprovImg(models.Model):
photo=models.ImageField(upload_to='products/%Y/%m/%d')
def __str__(self):
return str(self.photo)
w_p.html
<tr>
<td>{{ product.name }}</td>
<td> {{ product.price }}</td>
<td><form action="w_p.html" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit">
</form>
</td>
</tr
Can someone please help?
You should inherit from ModelForm.
class ApproveImgForm(forms.ModelForm):
class Meta:
model = ApprovImg
fields = "__all__" # not recommended, you should specify the fields.
# views.py
def upload_file(request):
if request.method == 'POST':
form = ApproveImgForm(request.POST, request.FILES)
if form.is_valid():
# file is saved
form.save()
return HttpResponseRedirect('/home/')
else:
form = ApproveImgForm
return render(request, 'upload_image.html', {'form': form})
# urls.py
urlpatterns = [path('upload', upload_file, name='upload')]
# upload_image.html
<form action="{% url 'upload' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.photo.label_tag }} {{ form.photo.help_text }}</p>
<p>
{{ form.photo.errors }}
{{ form.photo }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
I am building a frontend form that allows someone to post an article without accessing the admin.
When the user is logged in, I would like for him/her to be able to write an article. Upon saving, I would like that user to automatically be set as the author of the article.
I am at an impasse. Any help would be much appreciated.
models.py
from django.db import models
from django.urls import reverse
from django.contrib.auth.models import User
from django.utils import timezone
class Article(models.Model):
author = models.ForeignKey(User)
title = models.CharField(max_length=65)
text = HTMLField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
class ArticleImage(models.Model):
image = CloudinaryField('image')
image_name = models.CharField(max_length=55,
default='')
article = models.ForeignKey(Article)
def __str__(self):
return self.image_name
class ArticleTag(models.Model):
slug = models.SlugField(max_length=50,
unique=True)
article = models.ForeignKey(Article)
def __str__(self):
return self.slug
class ArticleCategory(models.Model):
slug = models.SlugField(max_length=20,
unique=True)
article = models.ForeignKey(Article)
def __str__(self):
return self.slug
forms.py
class ArticleCreationForm(ModelForm):
class Meta:
model = Article
fields = ['title', 'text']
widgets = {
'title': forms.TextInput(attrs={'placeholder': 'Please add a title. Max: 65 characters'}),
'text': forms.Textarea(attrs={'cols': 80, 'rows': 40, 'placeholder': 'Starting typing your article...'})
}
ArticleImageFormSet = inlineformset_factory(Article, ArticleImage,
fields=('image', 'image_name',),
extra=1,
max_num=1,
widgets={'image_name':
forms.TextInput(attrs={'placeholder': 'Image name'})})
ArticleTagFormSet = inlineformset_factory(Article, ArticleTag,
fields=('slug',),
extra=1,
max_num=1)
ArticleCategoryFormSet = inlineformset_factory(Article, ArticleCategory,
fields=('slug',),
extra=1,
max_num=1)
views.py
class CreateArticle(CreateView):
model = Article
form_class = ArticleCreationForm
template_name_suffix = '_add_form'
def get_success_url(self):
return reverse('accounts:detail', kwargs={'pk': self.object.pk})
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates blank versions of the form
and its inline formsets.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
articleimage_form = ArticleImageFormSet()
articletag_form = ArticleTagFormSet()
articlecategory_form = ArticleCategoryFormSet()
return self.render_to_response(
self.get_context_data(form=form,
articleimage_form=articleimage_form,
articletag_form=articletag_form,
articlecategory_form=articlecategory_form))
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
articleimage_form = ArticleImageFormSet(self.request.POST)
articletag_form = ArticleTagFormSet(self.request.POST)
articlecategory_form = ArticleCategoryFormSet(self.request.POST)
if (form.is_valid() and articleimage_form.is_valid() and
articletag_form.is_valid() and articlecategory_form.is_valid()):
return self.form_valid(form, articleimage_form, articletag_form,
articlecategory_form)
else:
return self.form_invalid(form, articleimage_form, articletag_form,
articlecategory_form)
def form_valid(self, form, articleimage_form, articletag_form,
articlecategory_form):
"""
Called if all forms are valid. Creates a Recipe instance along with
associated Ingredients and Instructions and then redirects to a
success page.
"""
self.object = form.save()
obj.author = request.user.username
articleimage_form.instance = self.object
articleimage_form.save()
articletag_form.instance = self.object
articletag_form.save()
articlecategory_form.instance = self.object
articlecategory_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, articleimage_form, articletag_form,
articlecategory_form):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(form=form,
articleimage_form=articleimage_form,
articletag_form=articletag_form,
articlecategory_form=articlecategory_form))
template.html
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<div class="row">
<div class="medium-9 columns">
{{ form.non_field_errors }}
<h2 class="article-identifier">Add a new article</h2>
<div class="fieldWrapper">
{{ form.title.errors }}
{{ form.title }}
<div id="title_feedback" class="text-right"></div>
</div>
<div class="fieldWrapper">
{{ form.text.errors }}
{{ form.text }}
</div>
</div>
<div class="medium-3 columns">
<div class="button-wrapper">
<input class="button" type="submit" value="Publish">
</div>
<fieldset class="image_upload">
<h2>Add an Image</h2>
{{ articleimage_form.management_form }}
{{ articleimage_form.non_form_errors }}
{% for form in articleimage_form %}
{{ form.id }}
<div class="inline {{ articleimage_form.prefix }}">
{{ form.image.errors }}
{{ form.image.label_tag }}
{{ form.image }}
{{ form.image_name.errors }}
{{ form.image_name.label_tag }}
{{ form.image_name }}
</div>
{% endfor %}
</fieldset>
<fieldset>
<h2>Add a Category</h2>
{{ articlecategory_form.management_form }}
{{ articlecategory_form.non_form_errors }}
{% for form in articlecategory_form %}
{{ form.id }}
<div class="inline {{ articlecategory_form.prefix }}">
{{ form.slug.errors }}
{{ form.slug.label_tag }}
{{ form.slug }}
</div>
{% endfor %}
</fieldset>
<hr />
<fieldset>
<h2>Add a Tag</h2>
{{ articletag_form.management_form }}
{{ articletag_form.non_form_errors }}
{% for form in articletag_form %}
{{ form.id }}
<div class="inline {{ articletag_form.prefix }}">
{{ form.slug.errors }}
{{ form.slug.label_tag }}
{{ form.slug }}
</div>
{% endfor %}
</fieldset>
</div>
</div>
</form>
Save the form with commit=False, set the user on the object, then save the object. Inside the form_valid method, you can access the user with self.request.user. You should assign the user instance, not the username as your code currently does.
obj = form.save(commit=False)
obj.author = self.request.user
...
obj.save
You should also restrict the view to logged in users. You can use the LoginRequiredMixin for this.
from django.contrib.auth.mixins import LoginRequiredMixin
class CreateArticle(LoginRequiredMixin, CreateView):
As per Django's documentation, you can just set form.instance.author to the current user (self.request.user), in the overriden form_valid method (you seem to have done something similar in your code already using other objects.). Then you can just return super().form_valid(form).
https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/#models-and-request-user
In your case you seem to have a need to do other things in your form_valid method, so it may not necessarily be correct for you to return super().form_valid(form).
I want to update a model entry using a form. the problem is that instead of updating the entry it creates a new entry.
def edit(request, c_id):
instance = get_object_or_404(C, id=int(c_id))
if request.POST:
form = CForm(request.POST, instance=instance)
if form.is_valid():
form.save()
return redirect('/a/b', c_id)
else:
form = CForm(instance=instance)
args = {}
args.update(csrf(request))
args['form'] = form
args['c_id'] = c_id
return render_to_response('a/b.html', args)
HTML code:
<form action="/a/edit/{{ c_id }}/" method="post">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
<input type="submit" value="Submit"/>
</form>
CForm class code
class CForm(forms.ModelForm):
class Meta:
model = C
fields = ['name', 'code']
You're checking the request for a POST method incorrectly. request.POST isn't a boolean, it contains a dictionary of post variables and is always going to have the CSRF token in it so it will always be "truthy". What you need is request.method.
Instead of:
if request.POST:
Replace it with:
if request.method == "POST":
So, everytime I submit my form, I always get this error
MultiValueDictKeyError at /template/ 'form-21-id'
Here is my views.py:
class SomeTemplate(TemplateView):
template_name = 'template/index.html'
def get_context_data(self, **kwargs):
context = super(AttendanceTemplate, self).get_context_data(**kwargs)
instruction = Instruction(self.request.user.username)
sections_list = self.request.GET.getlist('sections_list')
term = self.request.GET.get('term', instruction.term)
enrollments = Enrollment.objects.using('api').prefetch_related('profile').filter(section_id__in=['111111'], term=term)
attendanceQuery = Enrollment.objects.using('api').prefetch_related('student').filter(section_id__in=['111111'], term=term)
#logger.error(dir(attendanceQuery))
#logger.error(attendanceQuery.values())
for enrollment in attendanceQuery:
#logger.error(enrollment.student.first_name)
attendance, created = Attendance.objects.update_or_create(
section_id=enrollment.section_id,
term=enrollment.term,
first_name=enrollment.student.first_name,
last_name=enrollment.student.last_name,
email_address=enrollment.student.email_address,
meeting_date=timezone.now(),
section_info=3,
)
something = Attendance.objects.filter(section_id__in=['111111'], term=term)
formset = AttendanceFormSet(queryset=something)
combined = zip(enrollments, formset)
context['combined'] = combined
context['formset'] = formset
return context
def post(self, request):
formset = AttendanceFormSet(request.POST)
logger.error(formset.errors)
if formset.is_valid():
formset = formset.save();
return render_to_response("template/index.html", {'formset': formset},context_instance=RequestContext(request))
else:
return HttpResponse(request.POST)
Here is my index.html file:
<form method="POST" action="">
{% csrf_token %}
{{ formset.management_form }}
{% for enrollment, form in combined %}
{{ form.id }}
{% for hidden in combined %}
{{ hidden.hidden_fields }}
{% endfor %}
<div class="wrapper-formset">
<div>
{{ form.first_name.value }}
{{ form.last_name.value }}
{{ form.email_address.value }}
</div>
<div class="clear-all"></div>
</div>
{% endfor %}
<button type="submit" class="save btn btn-default">Save</button>
</form>
I've included the following key form properties in my template as well:
form.id
management form
hidden fields
What am I missing? Doing it wrong? DB issue?
How do I perform a Django form validation for an unknown number of fields specified by the user? In my case, form allows a user to create a music album with any number of tracks and associate it to an artist. The view asks for how many tracks there are and then generates a form with that many input fields.
Form:
class NumberOfTracks(forms.Form):
track_no = forms.IntegerField()
class CustomAlbumAdmin(forms.Form):
artist = forms.CharField(max_length=150)
album = forms.CharField(max_length=150)
track_no = forms.IntegerField()
track = forms.CharField(max_length=150)
View:
def album_admin(request):
if request.GET.get('track_no'):
number_of_tracks = request.GET.get('track_no')
artists = Artist.objects.all()
return render(request, 'customadmin/album_admin1.html', {
'number_of_tracks': number_of_tracks,
'tracks': range(1, int(number_of_tracks) + 1),
'artists': artists,
})
elif request.method == 'POST':
form = CustomAlbumAdmin(request.POST)
print form
artist = request.POST['artist']
album = request.POST['album']
all_tracks = request.POST.getlist('track')
create_album = CreateAlbum(artist=artist, album=album, tracks=all_tracks)
create_album.save_album()
create_album.save_tracks()
form = NumberOfTracks()
return render(request, 'customadmin/no_tracks1.html', {
'form': form,
})
else:
form = NumberOfTracks()
return render(request, 'customadmin/no_tracks1.html', {
'form': form,
})
(Just so it's clear, I used if form.is_valid() and form.cleaned_data but to get this to work thus far, I've had to bypass that in favor of getting the raw POST data)
Part of what's confusing me is that I've customized my form template to add a number input fields with name="track" depending on user input (EX: create an album with 13 tracks). When I go into my view to print form = CustomAlbumAdmin(request.POST) it gives a very simple table based on my form: one artist, one album, one track, and one track_no so validating against this will of course return False unless I have an album with just one track.
Here's the template:
{% extends 'base.html' %}
{% block content %}
<form action="/customadmin/album1/" method="POST">{% csrf_token %}
<select name="artist">
{% for entry in artists %}
<option value="{{ entry.name }}">{{ entry.name }}</option>
{% endfor %}
</select>
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.album.errors }}
<label for="id_album">Album:</label>
<input id="id_album" maxlength="150" name="album" type="text" />
</div>
<div class="fieldWrapper">
{{ form.track.errors }}
<input type="hidden" name="number_of_tracks" value="{{ number_of_tracks }}">
{% for e in tracks %}
<label for="id_track">Track No. {{ forloop.counter }}</label>
<input id="id_track_{{ forloop.counter }}" maxlength="150" name="track" type="text" /></br>
{% endfor %}
</div>
<p><input type="submit" value="Save album" /></p>
</form>
{% endblock %}
The one way I was thinking of approaching this was to create a custom clean_track method that takes a list of all the tracks entered as I've done in the view with all_tracks = request.POST.getlist('track') but not sure how to do that.
A related question I have is if I can customize validation based on POST data. The first way I approached this was to generate incremented inputs with name="track_1", name="track_2", etc.,. and then trying to validate based on that. However, I wouldn't be able to use request.POST.getlist('track') in that case.
It might be a better approach to use formsets instead.
class AlbumForm(forms.Form):
artist = forms.CharField(max_length=150)
name = forms.CharField(max_length=150)
class TrackForm(forms.Form):
track_no = forms.IntegerField()
name = forms.CharField(max_length=150)
# In view
from django.forms.formsets import formset_factory
TrackFormSet = formset_factory(TrackForm)
if request.method == 'POST':
track_formset = TrackFormSet(request.POST)
album_form = AlbumForm(request.POST)
if track_formset.is_valid() and album_form.is_valid():
save_album(album_form, track_formset)
else:
track_formset = TrackFormSet()
album_form = AlbumForm()
And in save_album you can just iterate through track_formset.ordered_forms to get each form in the formset:
for form in track_formset.ordered_forms:
data = form.cleaned_data
# Do what you want with the data
This can be even more powerful if you use model formsets because you can set a foreign key in the track model that points to the album model, and Django can save them automatically for you.