I'm currently learning the Python / Django stack by following some training to build a blog.
I currently have two similar views for adding new and editing existing posts (post_new and post_edit) as below:
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect("post_detail", pk=post.pk)
else:
form = PostForm()
return render(request, "blog/post_edit.html", {"form": form})
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect("post_detail", pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, "blog/post_edit.html", {"form":form})
Although these views do different jobs they share some identical code.
Trying to follow best practice (DRY), is there a sensible way to make such similar views DRYer? Or is it better to leave views of this sort of length in long form to keep them easy to read?
I would personally write it like this:
def post_edit(request, pk=None):
if pk is not None:
post = get_object_or_404(Post, pk=pk)
else:
post = None
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect("post_detail", pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, "blog/post_edit.html", {"form":form})
Basically, you pass the default instance value to the ModelForm.
You probably want to use Class-based views for that.
from django.views.generic.edit import CreateView, UpdateView
class PostCreate(CreateView):
model = Post
fields = ['name', ...]
class PostUpdate(UpdateView):
model = Post
fields = ['name', ...]
Related
I got this error in my functional view:
save() got an unexpected keyword argument 'commit'
I'm try to save one object in database. 'debtors' is Many to Many field in models.py.
forms.py
class ExpenseForm(forms.ModelForm):
class Meta:
model = Expense
fields = ('amount', 'text', 'debtors', 'date', 'time',)
widgets = {
'date': AdminDateWidget(),
'time': AdminTimeWidget(),
'debtors': forms.CheckboxSelectMultiple(),
}
views.py
def expenseformview(request, pk):
if request.method == 'POST':
form = Expense.objects.create(
expenser = request.user,
amount = request.POST.get('amount'),
text = request.POST.get('text'),
date = request.POST.get('date'),
time = request.POST.get('time'),
)
form.debtors.add(request.POST.get('debtors'))
formcoseshare = form.save(commit=False)
formcoseshare.save()
form.save_m2m()
return redirect('expense_detail', pk=pk, expenseid=form.id)
else:
form = ExpenseForm()
return render(request, 'financials/expense_form.html', {'form': form})
How can to solve this problem?
Your form is not an ExpenseForm, it is a model object Expense, hence commit=False makes no sense, and neither does .save_m2m():
from django.contrib.auth.decorators import login_required
#login_required
def expenseformview(request, pk):
if request.method == 'POST':
form = ExpenseForm(request.POST, request.FILES)
if form.is_valid():
form.instance.expenser = request.user
expense = form.save()
return redirect('expense_detail', pk=pk, expenseid=expense.pk)
else:
form = ExpenseForm()
return render(request, 'financials/expense_form.html', {'form': form})
It is however unclear to me what pk is doing here: you do not use it in any way.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
im using a non-model based form django.
once i get the data,i create a model object.
but when im trying to edit my post(a blog/quote based app),im not able to create a form object using the model object for a specific post.
these are my codes:
views.py:
def quote_form(request):
if request.method=='POST':
form=Quote(request.POST)
if form.is_valid():
quote=form.cleaned_data['quote']
author=form.cleaned_data['author']
popularity=form.cleaned_data['popularity']
category=form.cleaned_data['category']
p=Quote1(quote=quote, author=author, popularity=popularity, category=category)
p.save()
return redirect("quote_list")
else:
form=Quote()
return render(request,'quote/form.html',{'form':form})
def quote_edit(request, pk):
q = get_object_or_404(Quote1, pk=pk)
if request.method == "POST":
form = Quote(request.POST,instance=q)
if form.is_valid():
q = form.save(commit=False)
q.author = request.user
q.save()
return redirect('quote_detail', pk=q.pk)
#return render(request,"blog/post_detail.html",{'post':post})
else:
form = Quote(instance=q)
return render(request, 'quote/quote_edit.html', {'form': form})
models.py:
class Quote1(models.Model):
quote=models.CharField(max_length=200)
author=models.CharField(max_length=200)
popularity=models.IntegerField()
category=models.CharField(max_length=40)
forms.py:
class Quote(forms.Form):
quote=forms.CharField()
author=forms.CharField()
popularity=forms.IntegerField()
category=forms.ChoiceField(choices=[('life','life'),('happiness','happiness'),('love','love'),('truth','truth'),
('inspiration','inspiration'),('humor','humor'),('philosophy','philosophy'),('science','science')])
Try this:
def quote_edit(request, pk):
q = get_object_or_404(Quote1, pk=pk)
if request.method == "POST":
form = Quote(request.POST)
if form.is_valid():
quote=form.cleaned_data['quote']
author=form.cleaned_data['author']
popularity=form.cleaned_data['popularity']
category=form.cleaned_data['category']
q.quote=quote
q.author=author
q.popularity=popularity
q.category=category
q.save()
else:
form = Quote(initial=reauest.POST.copy())
return render(request, 'quote/quote_edit.html', {'form': form})
P.S:
Using ModelForm would have been better approach. If you can switch to ModelForm i can help there as well.
I'm fairly new to coding in general, so please forgive my ignorance.
I have a ModelForm (Django 1.10 -just in case-) for a 'post' on a social network website:
models.py:
class Post(models.Model):
user = models.ForeignKey(User)
text = models.TextField()
image = models.ImageField(blank=True)
draft = models.BooleanField(default=False)
submission_date = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.text
forms.py:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['text', 'image', 'draft']
To update a post, this is its function:
views.py:
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if not request.user.is_superuser and\
not request.user == post.user:
raise PermissionDenied
if request.method == 'POST':
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
context = {'post': post,'form': form}
return HttpResponse(render(request, 'accounts/view_post.html', context))
elif request.method == 'GET':
form = PostForm(request.GET, instance=post)
context = {'post': post,'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
in the template:
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'accounts/form_template.html' %}
<input class="btn btn-btn-success" type="submit" value="Save Post">
</form>
My question is: when trying to update, why does the original field input not show up? The fields turn up empty as if I were creating a new post.
The more detailed answer, the more I would appreciate it.
Thank you in advance.
Turns out all I needed to do was remove 'request.GET':
in views.py:
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
...
...
elif request.method == 'GET':
form = PostForm(instance=post)
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
Thank you to everyone who tried to help, especially #Zagorodniy Olexiy.
If you want to show original inputs in the form you have to ad some code in your PostForm:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['text', 'image', 'draft']
def __init__(self, *args, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
then you have to load it in to the view
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if not request.user.is_superuser and\
not request.user == post.user:
raise PermissionDenied
if request.method == 'POST':
form = PostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/view_post.html', context))
elif request.method == 'GET':
form = PostForm(request.GET, instance=post)
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
I want to pass a pk from one form to another so that it can be used as the foreign key for the second form. Here are the model:
models.py
class CompanyDetails(models.Model):
name = models.CharField(max_length=100)
class CompanyDetailsForm(forms.ModelForm):
class Meta:
model = CompanyDetails
class DataRequest(models.Model):
company = models.ForeignKey(CompanyDetails, default="0")
agency_name = models.CharField(max_length=100)
class DataRequestForm(forms.ModelForm):
class Meta:
model = DataRequest
exclude = ['company']
And here is the view for the first form:
views.py
def index(request):
if request.method == 'POST':
form = CompanyDetailsForm(request.POST or None)
if form.is_valid():
data = form.save(commit=False)
data.save()
return HttpResponseRedirect(reverse('canareeform:datarequest', data.id))
else:
form = CompanyDetailsForm()
return render(request, 'canareeform/index.html', {'form': form})
How should I set up my second view so that the form will save an object that has the foreign key for the object created by the first form in it?
I got it to work by passing the primary key of the first object through the url. It goes abc.com/form -> abc.com/form/16/datarequest. Not super ideal since by changing the number in the url the second object will use some other foreign key.
views.py
def index(request):
if request.method == 'POST':
form = CompanyDetailsForm(request.POST or None)
if form.is_valid():
data = form.save(commit=False)
data.save()
return HttpResponseRedirect(reverse('canareeform:datarequest', args=(data.id,)))
else:
form = CompanyDetailsForm()
return render(request, 'canareeform/index.html', {'form': form})
def datarequest(request, company_id):
if request.method == 'POST':
form = DataRequestForm(request.POST or None)
if form.is_valid():
data = form.save(commit=False)
data.company = CompanyDetails.objects.get(pk=company_id)
data.save()
return HttpResponse("Thanks")
else:
form = DataRequestForm()
return render(request, 'canareeform/datarequest.html', {'form': form})
If anyone has a better solution I'd love to hear it.
The form is submitting a blog post and redirecting towards the index page when submitted.
How do I change it so that it redirects to the newly submitted blog post
views.py
def post(request, post_url):
single_post = get_object_or_404(Post, title=post_url.replace('_', ' '))
popular_posts = Post.objects.order_by('-views')[:5]
single_post.views+=1
single_post.save()
t=loader.get_template('blog/post.html')
c = Context({'single_post': single_post, "popular_posts":popular_posts, })
return HttpResponse(t.render(c))
def add_post(request):
context = RequestContext(request)
if request.method =='POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
form.save(commit=True)
return redirect(index)
else:
print form.errors
else:
form = PostForm()
return render_to_response('blog/add_post.html', {'form':form}, context)
considering you have a blog post url in form of :
url(r'^blog/(?P<blog_id>[0-9]{4})/$', views.blog_detail, name='blog_detail'),
then,
def add_post(request):
context = RequestContext(request)
if request.method =='POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
obj = form.save(commit=True)
return HttpResponseRedirect(reverse('blog_detail', obj.id))
else:
print form.errors
else:
form = PostForm()
return render_to_response('blog/add_post.html', {'form':form}, context)
Check the get_absolute_url method for django model objects
def add_post(request):
context = RequestContext(request)
if request.method =='POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
form.save(commit=True)
return redirect(form.instance.get_absolute_url())
else:
print form.errors
else:
form = PostForm()
return render_to_response('blog/add_post.html', {'form':form}, context)