django is there better variant to use CBV approach in my project? - django

Sorry for such a long question and my bad english. I have finished Python Crash Course, an introductory programming book by Eric Matthes. After that decided to continue study Django and found that CBV method is more acceptable for site creating. I rewrited via CBV training program from the book which was written by functions, but I still feel a bit lost with methods of CBV after reading the official documentation.
Could somebody tell, is there a lot of hardcoding in my CBV variant? And it's possible to do it better ?
Every variant works fine.
Here the variant of views from the books with comments, I inserted a comments to understand what code does:
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.http import Http404
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
# Create your views here.
def index(request):
"""Home page of Learning Log."""
return render(request, 'learning_logs/index.html')
def more_inf(request):
return render(request,'learning_logs/more_inf.html')
def topics(request):
"""List of topics"""
public_topics = Topic.objects.filter(public=True).order_by('date_added')
if request.user.is_authenticated:
private_topics = Topic.objects.filter(owner=request.user).order_by('date_added')
topics = public_topics | private_topics
else:
topics = public_topics
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
#login_required
def topic(request, topic_id):
"""Show one topic with details"""
topic = get_object_or_404(Topic, id=topic_id)
#Проверка того, что тема принадлежит текущему пользователю
check_topic_owner(topic.owner, request)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
#login_required
def new_topic(request):
"""Create new topic"""
if request.method != 'POST':
#data wasn't sent;create empty form
form = TopicForm()
else:
#POST data sent; to process the data.
form = TopicForm(data=request.POST)
if form.is_valid():
new_topic = form.save(commit=False)
new_topic.owner = request.user
new_topic.save()
return redirect('learning_logs:topics')
#Show empty or invalid form.
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
#login_required
def new_entry(request, topic_id):
"""Add new entry to the topic"""
topic = get_object_or_404(Topic, id=topic_id)
check_topic_owner(topic.owner, request)
if request.method != 'POST':
#data wasn't sent;create empty form
form = EntryForm()
else:
#POST data sent; to process the data.
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return redirect('learning_logs:topic', topic_id=topic_id)
#Show empty or invalid form.
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
#login_required
def edit_entry(request, entry_id):
"""Edit the current entry"""
entry = get_object_or_404(Entry, id=entry_id)
topic = entry.topic
check_topic_owner(topic.owner, request)
if request.method !='POST':
#initial request; form was created by current data entries
form = EntryForm(instance=entry)
else:
#POST data sent; to process the data.
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return redirect('learning_logs:topic', topic_id=topic.id)
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'learning_logs/edit_entry.html', context)
def check_topic_owner(owner, request):
if owner != request.user:
raise Http404
There is my new CBV variant, also with slugs added and absolute_url:
What can I improve there? Thank you in advance
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import Http404
from django.views.generic import ListView, DetailView, CreateView, UpdateView
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
# Create your views here.
def index(request):
"""Домашняя страница приложения Learning Log."""
return render(request, 'learning_logs/index.html')
def more_inf(request):
return render(request,'learning_logs/more_inf.html')
class TopicsHome(ListView):
model = Topic
template_name = 'learning_logs/topics.html'
context_object_name = 'topics'
def get_queryset(self):
public_topics = Topic.objects.filter(public=True).order_by('date_added')
if self.request.user.is_authenticated:
private_topics = Topic.objects.filter(owner=self.request.user).order_by('date_added')
topics = public_topics | private_topics
else:
topics = public_topics
return topics
class ShowTopic(LoginRequiredMixin, DetailView):
model = Topic
template_name = 'learning_logs/topic.html'
context_object_name = 'topic'
slug_url_kwarg = 'topic_slug'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
topic = get_object_or_404(Topic, slug=self.kwargs['topic_slug'])
check_topic_owner(topic.owner, self.request)
context['entries'] = topic.entry_set.order_by('-date_added')
return context
class AddTopic(LoginRequiredMixin, CreateView):
form_class = TopicForm
template_name = 'learning_logs/new_topic.html'
def form_valid(self, form):
new_topic = form.save(commit=False)
new_topic.owner = self.request.user
new_topic.save()
return redirect('learning_logs:topics')
class AddEntry(LoginRequiredMixin, CreateView):
form_class = EntryForm
template_name = 'learning_logs/new_entry.html'
slug_url_kwarg = 'topic_slug'
def form_valid(self, form):
topic = get_object_or_404(Topic, slug=self.kwargs['topic_slug'])
check_topic_owner(topic.owner, self.request)
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return redirect('learning_logs:topic', topic_slug=topic.slug)
class EditEntry(LoginRequiredMixin, UpdateView):
model = Entry
form_class = EntryForm
template_name = 'learning_logs/edit_entry.html'
context_object_name = 'topic'
slug_url_kwarg = 'entry_slug'
def form_valid(self, form):
entry = get_object_or_404(Entry, slug=self.kwargs['entry_slug'])
topic = entry.topic
check_topic_owner(topic.owner, self.request)
form.save()
return redirect('learning_logs:topic', topic_slug=topic.slug)
def check_topic_owner(owner, request):
if owner != request.user:
raise Http404

Function Based Views (FBV) and Class Based Views (CBV) both have their place in Django projects. It usually boils down to developer preference. Personally, I prefer FBV because there is less abstraction. Though there is nothing wrong with going the CBV route either!
If you want to read more about FBV and why one might prefer this, check out this series of articles: https://spookylukey.github.io/django-views-the-right-way/

Related

Chained dropdown validation error. Form works

Creating a chained drop down following a tutorial. Everything works fine but it is throwing an unnecessary validation error.
It is a basic 3 field form creating a person object with name, country and city.
Views.py
from django.shortcuts import render, redirect, get_object_or_404
from .forms import CustomerForm
from .forms import PersonCreationForm
from .models import Person, City
def store(request):
form = CustomerForm()
context = {'form': form}
return render(request, 'store/store.html', context)
def cart(request):
form = CustomerForm()
context = {'form': form}
return render (request, 'store/cart.html', context)
def checkout(request):
form = CustomerForm()
context = {'form': form}
return render(request, 'store/checkout.html', context)
def signup(request):
context = {}
return render(request, 'store/signup.html', context)
def home(request):
if request.method == 'POST':
form = CustomerForm(request.POST)
if form.is_valid():
pass # does nothing, just trigger the validation
else:
form = CustomerForm()
return render(request, 'shop.html', {'form': form})
def person_create_view(request):
form = PersonCreationForm()
if request.method =='POST':
form = PersonCreationForm(request.POST)
if form.is_valid():
form.save()
return redirect('person_add')
return render(request, 'store/ho.html', {'form': form})
def person_update_view(request, pk):
person = get_object_or_404(Person, pk = pk)
form = PersonCreationForm(instance = person)
if request.method == 'POST':
form = PersonCreationForm(request.POST, instance=person)
if form.is_valid():
form.save()
return redirect('person_change', pk = pk)
return render(request, 'store/ho.html', {'form': form})
#ajax
def load_cities(request):
country_id = request.GET.get('country_id')
cities = City.objects.filter(country_id = country_id)
return render(request, 'store/city_dropdown_list_options.html',{'cities':cities})
Forms.py
from django.forms import ModelForm
from .models import Customer, Person, City
from django import forms
class CustomerForm(ModelForm):
class Meta:
model = Customer
fields = '__all__'
class PersonCreationForm(forms.ModelForm):
class Meta:
model = Person
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['city'].queryset = City.objects.none()
if 'country' in self.data:
try:
country_id = int(self.data.get('country'))
self.fields['city'].queryset = City.objects.filter(country_id = country_id)
except(ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['city'].queryset = self.instance.country.city_set.order_by('name')
In forms.py line 14 where it says-
self.fields['city'].queryset = City.objects.none()
this line is throwing a validation error when I try to submit the form. In the tutorial they add the if/else argument and that solves the problem and error goes away.
error: "Select a valid choice. That choice is not one of the available choices"
In my case the error still shows up. If I ignore it and re select the city and submit, it works just fine.
It is almost like having a pause at that line and the system suddenly realizes that there is more code.
How do I fix this problem. Appreciate any help.

The view blogapp.views.blogpost didn't return an HttpResponse object. It returned None instead

from django import forms
from .models import Blog
I just rewrite code this point,
from ↓:
# class BlogPost(forms.ModelForm):
# class Meta:
# model = Blog
# fields = ['title', 'body']
to ↓:
class BlogPost(forms.Form):
email = forms.EmailField()
files = forms.FileField()
url = forms.URLField()
words = forms.CharField(max_length=200)
max_number = forms.ChoiceField(choices=[('1','one'), ('2','two'),('3','three')])
and views.py file is here ↓:
from django.shortcuts import render, get_object_or_404, redirect
from .models import Blog
from django.utils import timezone
from .form import BlogPost
from django.views.decorators.http import require_http_methods
# Create your views here.
def home(request):
blogs = Blog.objects #쿼리셋
return render(request, 'home.html', {'blogs'`enter code here`:blogs})
def detail(request, blog_id):
details = get_object_or_404(Blog, pk=blog_id)
return render(request, 'detail.html', {'details':details})
def new(request):
return render(request, 'new.html')
def create(request):
blog = Blog()
blog.title = request.GET['title']
blog.body = request.GET['body']
blog.pub_date = timezone.datetime.now()
blog.save()
return redirect('/blog/'+str(blog.id)) #이 URL로 넘기세요 처리된 정보를
def blogpost(request):
if request.method =='POST':
form = BlogPost(request.POST)
if form.is_valid():
post = form.save(commit=False)
# post.pub_date = timezone.now()
post.save()
return redirect('home')
else:
form = BlogPost()
return render(request, 'newblog.html',{'form':form})
I don't know why I only rewrite 'form.py' file, but vscode program says "The view blogapp.views.blogpost didn't return an HttpResponse object. It returned None instead."
what should I do?? help..
def blogpost(request):
if request.method =='POST':
form = BlogPost(request.POST)
if form.is_valid():
post = form.save(commit=False)
# post.pub_date = timezone.now()
post.save()
return redirect('home')
else:
form = BlogPost()
return render(request, 'newblog.html',{'form':form})
You don't have a return statement on here due to your if statement and indentation.
Write it like:
def blogpost(request):
if request.method =='POST':
form = BlogPost(request.POST)
if form.is_valid():
post = form.save(commit=False)
# post.pub_date = timezone.now()
post.save()
return redirect('home')
else:
form = BlogPost()
return render(request, 'newblog.html',{'form':form})
And it should work.

RelatedObjectDoesNotExist at /post/new/ - Post has no author

I have implemented allauth for user registration and now I am trying to associate the current_user to a post when its being created. When I press 'Submit' I get 'Post has no author' clearly the posts don't recognize the user.
Model:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
def __str__(self):
return self.title
View:
def create_post(request, pk=None):
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author(request.user)
post.save()
else:
form = PostForm()
context = {'form' : form}
return render(request, 'blog/create_post.html', context)
I could really use some pointers, many thanks.
You can set the author instance of the .instance wrapped in the form:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def create_post(request, pk=None):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.instance.author = request.user
form.save()
return redirect('name-of-a-view')
else:
form = PostForm()
context = {'form' : form}
return render(request, 'blog/create_post.html', context)
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.

form validation Not Working Django and print just on message

Django form validation is not working. It only prints the following message:
form is not valid!
I am a Django beginner and I am having trouble figuring out why this is happening.
mycode in form.py:
efrom django import forms
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.forms.widgets import CheckboxSelectMultiple
from blog.models import Category
from wsgiref.validate import validator
class PostForm(forms.Form):
title = forms.CharField(max_length = 100, min_length= 1,)
slug = forms.SlugField(allow_unicode=True)
message = forms.CharField(widget = forms.Textarea)
banner = forms.ImageField()
category = forms.ModelChoiceField(queryset=Category.objects)
authors = forms.ModelChoiceField(queryset=User.objects,
widget=CheckboxSelectMultiple)
def clean(self):
cleaned_data = super(PostForm, self).clean()
slug = cleaned_data['slug']
if not "sepehr" in slug:
raise forms.ValidationError("here we have some errors !")
return cleaned_data
and my code in views:
class HoemView(generic.TemplateView):
template_name = 'blog2/create.html'
def get(self, request):
form = PostForm()
context = {
'form':form
}
return render(request, self.template_name, context)
def post(self, request):
form = PostForm(request.POST, request.FILES)
if form.is_valid():
return HttpResponse('form is valid!')
return HttpResponse('form is not valid!')
All you are seeing is 'form is not valid!' because that is all your view is returning in the HttpResponse.
Change the last line from:
return HttpResponse('form is not valid!')
to:
return HttpResponse(form.errors)
That should show you what your errors are.

django too many redirects after login

I am running: Django==2.0.6 / python==3.6.5 / django-allauth==0.36.0
I have not touched this project in several weeks. I upgraded a number of packages before starting this round of development.
I can currently create a new user account and verify the email address used as the username. The user model is "extended" in that I use the email address for the username and have a "Profile" class to hold additional interesting fields.
I was once able to create a new account, verify the email address and be taken to a form to fill out that additional interesting info. Now, after email verification there is a noticable pause and then I am told that I have ben redirected too many times.
My urls.py looks like:
from django.urls import path, re_path
from .views import index
from .views import profile
urlpatterns = [
re_path(r'^', index, name='index'),
path('Profile/<uuid:account_number>/edit', profile.Update.as_view(), name="profile-update"),
path('Profile/<uuid:account_number>/delete', profile.Delete.as_view(), name="profile-delete"),
path('Profile/create', profile.Create.as_view(), name="profile-create"),
]
The Profile CBV is:
import django.contrib.messages as messages
from allauth.account.decorators import verified_email_required
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.views.generic import CreateView, UpdateView, DeleteView
from Members.forms.profile import ProfileForm
from Members.models.profile import Profile
import datetime
#method_decorator(verified_email_required, name='dispatch')
class Create(CreateView):
model = Profile
template_name = 'Members/profile.html'
form_class = ProfileForm
success_url = 'Members/index.html'
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {
'profileForm': self.form_class(),
'profileState': "create"
})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.user = self.request.user
profile.save()
my_render = render(request, self.success_url, {
'profile': profile,
'profileState': "display"
})
else:
#
# form has issues. send it back to the create form to fix.
my_render = render(request, self.template_name, {
'profileForm': form,
'profileState': "editForCreate"
})
return my_render
#method_decorator(verified_email_required, name='dispatch')
class Update(UpdateView):
pk_url_kwarg = "account_number"
model = Profile
template_name = 'Members/profile.html'
form_class = ProfileForm
success_url = 'Members/index.html'
def get(self, request, *args, **kwargs):
profile = Profile.objects.get(account_number=self.kwargs[self.pk_url_kwarg])
form = ProfileForm(instance=profile)
return render(request, self.template_name, {
'profileForm': form,
'profileState': "edit"
})
def post(self, request, *args, **kwargs):
profile = Profile.objects.get(account_number=self.kwargs[self.pk_url_kwarg])
form = ProfileForm(request.POST, instance=profile)
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.dateModified = datetime.datetime.now()
profile.save()
my_render = render(request, 'Members/index.html', {
'profile': profile
})
else:
#
# form has issues. send it back to the create form to fix.
my_render = render(request, self.template_name, {
'profileForm': form,
'profileState': "editForUpdate"
})
return my_render
#method_decorator(verified_email_required, name='dispatch')
class Delete(DeleteView):
pk_url_kwarg = "account_number"
model = Profile
# form_class = ProfileForm
success_url = "account/login.html"
def get(self, request, *args, **kwargs):
try:
#
# I can't believe django does not honor on_delete cascade.
# Have to roll your own. Tsk
owner = self.request.user
profile = get_object_or_404(Profile, account_number=self.kwargs[self.pk_url_kwarg])
user_pk = profile.user.pk
profile.delete()
get_object_or_404(User, pk=user_pk).delete()
messages.success(request, "The user is deleted")
except User.DoesNotExist:
messages.error(request, "User does not exist")
# except IntegrityError:
# messages.error(request, "DB IntegrityError")
return redirect("accounts/login/")
How do you go about debugging this? Ive tried setting breakpoints in PyCharm but they are ignored or ar in the wrong places.
in your urls.py you need to add $ in order to close your regular expression
urlpatterns = [
re_path(r'^$', index, name='index'),
]