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

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.

Related

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

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/

Django. Populate user name or ID when user saving a model from web pages

My UserImg Model has a user field that has editable=False.
I want this field to be automatically filled in with the user name when the user is saved from web page.
model.py
def upload_myimg_path(instance, filename):
return 'documents/{0}/{1}'.format(instance.created_by.username, filename)
class UserImg(models.Model):
user = models.ForeignKey(User, verbose_name=_('Created by'), on_delete=models.CASCADE, editable=False, null=True, blank=True)
name = models.CharField(max_length=100, default='')
image = models.ImageField(upload_to=upload_myimg_path, verbose_name=_('File'))
def __str__(self):
return str(self.user)
forms.py
class UserImgForm(forms.ModelForm):
class Meta:
model = UserImg
fields = '__all__'
views.py
def createuserimg(request):
if request.method == 'POST':
form = UserImgForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('/accounts/users')
else:
return redirect('/accounts/')
else:
form = UserImgForm
return render(request, 'accounts/user_form.html', {'form': form})
Update your view function to include current logged in user and make use of #login_required decorator to ensure that only logged in users can access this view :
from django.contrib.auth.decorators import login_required
#login_required
def createuserimg(request):
if request.method == 'POST':
form = UserImgForm(request.POST, request.FILES)
if form.is_valid():
obj = form.save(commit=False) # <-- commit=False does not save to database
obj.user = request.user # <-- this allows you to specify the user for your post
obj.save()
return redirect('/accounts/users')
# if the form did not validated, stay on the same page to display errors to your user
else:
form = UserImgForm()
return render(request, 'accounts/user_form.html', {'form': form})
correct answer commit=False allows you to modify the resulting object before it is actually saved to the database. It`s works for me.
Thank you very much for your help
from django.contrib.auth.decorators import login_required
#login_required
def createuserimg(request):
if request.method == 'POST':
form = UserImgForm(request.POST, request.FILES)
if form.is_valid():
link = form.save(commit=False)
link.user = request.user
link.save()
return redirect('/accounts/users')
# if the form did not validated, stay on the same page to display errors to your user
else:
form = UserImgForm()
return render(request, 'accounts/user_form.html', {'form': form})

how to pass value from views to non-editable field django python

i want to auto insert user to the follwing model:
class Moduls(models.Model):
module_name = models.CharField(max_length=255)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, db_column='created_by', blank=False, null=False , on_delete=models.CASCADE, editable = False)
i have 2 cases :
1- in django admin and i solve this case by the following
* add editable = False to created by field
* in admin.py i used the follwoing:
def save_model(self, request, obj, form, change):
obj.created_by = request.user
super().save_model(request, obj, form, change)
but i face the problem when i try to use forms.py i can't pass user from user views.py and if i want to add created_by field to my form i have this error:
'created_by' cannot be specified for Moduls model form as it is a non-editable field
You normally do not set that in the form, but in the view. So you define a form that looks like:
class ModulsForm(forms.ModelForm):
class Meta:
model = Moduls
fields = ['module_name']
In the view, you then set the created_by field of the instance wrapped in the view. So for a function-based view, it looks like:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def some_view(request):
if request.method == 'POST':
form = ModulsForm(request.POST, request.FILES)
if form.is_valid():
form.instance.created_by = request.user
form.save()
return redirect('name-of-some-view')
else:
form = ModulsForm()
return render(request, 'name-of-some-template.html', {'form': form})
or in a class-based view, for example a CreateView, you override the form_valid method:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
class ModulsCreateView(LoginRequiredMixin, CreateView):
model = Moduls
form_class = ModulsForm
# …
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)

Django CRUD - create and update view doesn't work

I'm really new to Django and I want to teach myself by making a simple note. I maked a simple form for creating a new note but I don't know how to make this just for the user is logged in. What I mean is that I want the user field from the creationNoteForm to be removed and the note to be sumbitted automatically for the person who is logged in. Hope that I was clear enough.
Here is my "view.py":
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login, logout
from .forms import CreateUserForm, CreateNoteForm
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .models import *
# Create your views here.
def loginPage(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('home')
else:
messages.info(request, 'Username or pasword is incorrect')
context = {}
return render(request, 'accounts/login.html', context)
def registerPage(request):
form = CreateUserForm()
if request.method == 'POST':
form = CreateUserForm(request.POST)
if form.is_valid():
form.save()
user = form.cleaned_data.get('username')
messages.success(request, 'Account was created for '+ user)
return redirect('home')
context = {'form': form}
return render(request, 'accounts/register.html', context)
def logoutUser(request):
logout(request)
return redirect('login')
#login_required(login_url='login')
def home(request):
if request.user.is_authenticated:
username = request.POST.get('username')
context = {'username': username}
return render(request, 'accounts/home.html', context)
#login_required(login_url='login')
def notes(request):
username = None
if request.user.is_authenticated:
username = request.user.username
user_id = request.user.pk
user_notes = Note.objects.filter(user=user_id)
context = {
'user_notes': user_notes,
'username': username,
#'user_id' : user_id,
}
return render(request, 'accounts/notes.html', context)
def createNote(request):
# username = request.user.username
# user_id = request.user.pk
# user_notes = Note.objects.filter(user=user_id)
form = CreateNoteForm()
if request.method == 'POST':
form = CreateNoteForm(request.POST)
if form.is_valid():
form.save()
return redirect('notes')
context = {
'form': form,
}
return render(request, 'accounts/create_note.html', context)
def deleteNote(request, pk):
note = Note.objects.get(id=pk)
if request.method == 'POST':
note.delete()
return redirect('notes')
context = {'note': note}
return render(request, 'accounts/delete.html', context)
def updateNote(request, pk):
note = Note.objects.get(id=pk)
form = CreateNoteForm(instance=note)
if request.method == 'POST':
form = CreateNoteForm(request.POST, instance=note)
if form.is_valid():
form.save()
return redirect('notes')
context = {'form': form, 'note': note}
return render(request, 'accounts/update.html', context)
"forms.py":
from django.forms import ModelForm
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from .models import Note
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class CreateNoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ['user', 'title', 'text']
and "models.py" just in case:
from django.db import models
from django.contrib.auth.models import User
class Note(models.Model):
#user = models.ForeignKey(Person, null=True, on_delete=models.SET_NULL)
user = models.ForeignKey(User, null=True,on_delete=models.SET_NULL)
title = models.CharField(max_length=200, null=True)
text = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Thank you and sorry I am a noob!

How to save the users last logout time

I am looking to save the users last logout time.My idea was to add it to the users profile model. I am using Django 1.11.15
Example:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
last_logout = models.DateTimeField(blank=True, null=True)
below is how I created my profile model just for reference
def signup(request):
if request.method == 'POST':
form = UserCreateForm(request.POST or None)
if form.is_valid():
new_user = form.save()
Profile.objects.create(user=new_user)
return redirect('accounts:edit_profile')
else:
form = UserCreateForm()
context = {'form': form}
return render(request, 'accounts/signup.html', context)
Below is what I intend to do. Is this the correct way. I want to add to django's default signout/logout method I am not sure if its called signout or logout
class LoggedOut(TemplateView):
template_name = 'logged_out.html'
def signout(self):
"""logout user """
self.request.user.profile.last_logout = datetime.now()
self.request.user.profile.save()
My URL's
url(r'^loggedout/$', views.LoggedOut.as_view(), name='loggedout'),
You can use Django logout signal for such purpose.
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.dispatch import receiver
#receiver(user_logged_out)
def sig_user_logged_out(sender, user, request, **kwargs):
user.profile.last_logout = datetime.now()
user.profile.save()