Django database relation - python

I am working on a forum using django, I have a problem accessing user fullname and bio, from a model class I have. I have no problem accessing the user.username or user.email, but not from the Author class..
This is from the models.py in the forum app
User = get_user_model()
class Author(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
fullname = models.CharField(max_length=40, blank=True)
slug = slug = models.SlugField(max_length=400, unique=True, blank=True)
bio = HTMLField()
points = models.IntegerField(default=0)
profile_pic = ResizedImageField(size=[50, 80], quality=100, upload_to="authors", default=None, null=True, blank=True)
def __str__(self):
return self.fullname
My form is in the user app, where i have a profile update site, and the form is like this
from forums.models import Author
class UpdateForm(forms.ModelForm):
class Meta:
model = Author
fields = ('fullname', 'bio', 'profile_pic')
Then here is some of the update site, however nothing let me get access to the bio or fullname, I've tried so many combos. and I am lost here..
{% block content %}
<section class="section" id="about">
<!-- Title -->
<div class="section-heading">
<h3 class="title is-2">Hey {{ user.username }}</h3>
<div class="container">
<p>{{ user.bio }}bio comes here</p>
</div>
</div>
Here is the view.py from the user app
from .forms import UpdateForm
def update_profile(request):
context = {}
user = request.user
instance = Author.objects.filter(user=user).first()
if request.method == 'POST':
form = UpdateForm(request.POST, request.FILES, instance=user)
if form.is_valid():
form.instance.user = user
form.save()
return redirect('/')
else:
form = UpdateForm(instance=user)
context.update({
'form': form,
'title': 'update_profile',
})
return render(request, 'register/update.html', context)
The form html
<form method="POST" action="." enctype="multipart/form-data">
{% csrf_token %}
{{form|crispy}}
<hr>
<button class="button is-block is-info is-large is-fullwidth">Update <i class="fa fa-sign-in" aria-hidden="true"></i></button>
</form>
If there is some relation i am missing please help

Your view currently creates a new Author record each time you "update" the model. I would advise to first clean up the database and remove all authors.
Then you can convert the ForeignKey into a OneToOneField here: that way we know that each user has at most one Author:
from django.conf import settings
class Author(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
# …
Now we can alter the view to create a record in case there is no such record, or update an existing record if there is one:
from .forms import UpdateForm
def update_profile(request):
context = {}
user = request.user
instance = Author.objects.filter(user=user).first()
if request.method == 'POST':
form = UpdateForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
form.instance.user = user
form.save()
return redirect('/')
else:
form = UpdateForm(instance=instance)
context.update({
'form': form,
'title': 'update_profile',
})
return render(request, 'register/update.html', context)
In the template, you can render data of the related Author model for a user user with:
{{ user.author.fullname }}
{{ user.author.bio }}

Related

how to auto-populate slug field in django forms

i want to allow users of a website create blog post from a form that i made, but the slug field does not get populated automatically from the frontend but from the backend (admin page) it does get populated, and that is not what i want. I want the slug field to get populated with the title when the users want to create a post e.g this-is-an-example please how do i go about it
models.py
class Blog(models.Model):
title = models.CharField(max_length=10000, null=True, blank=True, verbose_name="Title")
slug = models.SlugField(unique=True)
content = RichTextField()
image = models.ImageField(upload_to="blog-images/%Y/%m/%d/", verbose_name="Post Thumbnail")
def get_absolute_url(self):
return reverse("blog:blog-details", args=[self.slug])
def __str__(self):
return self.title
views.py
#login_required
def new_post(request):
info = Announcements.objects.filter(active=True)
categories = Category.objects.all()
if request.method == "POST":
form = BlogPostForm(request.POST, request.FILES)
if form.is_valid():
form.instance.creator = request.user
form.save() # ← no commit=False
messages.success(request, f'Hi, Your Post have been sent for review and would be live soon!')
return redirect('blog:home')
else:
form = BlogPostForm()
context = {
'form': form,
'info': info,
'categories': categories
}
return render(request, 'blog/newpost.html', context)
forms.py NOTE if i remove the 'slug' from the field it throws an error saying that slug is needed
class BlogPostForm(forms.ModelForm):
class Meta:
model = Blog
fields = ('title', 'slug', 'content', 'image', 'category')
newpost.html
<form action="" method="POST" enctype="multipart/form-data">
<p><span style="color: black;"><b>NOTE</b></span>: For slug field, input the title as slug field but with hypens in between text <br> e.g <span style="color: black;"><b> "this-is-a-new-post"</b></span></p>
{% csrf_token %}
{{form|crispy}}
{{form.media}}
<div class="form-group">
<button class="btn theme-bg rounded" type="submit">Post Content</button>
</div>
</form>
Django already has a method for slugs. You can override your save model method and add the slugify field.
from django.utils.text import slugify
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)

Does not reflect current user in comment

I'm creating an Instagram-style application with Django 3.2.5, where users have profiles to visit and upload photos. I am implementing a commenting system.
When generating the comment as a user with a registered profile, the comment is rendered as if it were anonymous.
I have investigated and it happens that in the model of the comment I made a relationship with the user and profile giving the null parameter as true.
When removing this parameter and resubmitting a comment on a post I get a Not Null Constraint Failed error for profile_id and for user_id.
The image shows the first comments made from the admin. And the last one with the null = true parameter in the model.
Sample picture
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
text = models.TextField(max_length=200)
created_date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_date']
def __str__(self):
return self.text
views.py
def create_comment(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('posts:feed')
else:
form = CommentForm()
return render(request, 'posts/feed.html', {
'form': form,
})
urls.py
urlpatterns = [
path(r'^post/(?P<pk>[0-9]+)/comment/$', create_comment, name='create'),]
forms.py
class CommentForm(forms.ModelForm):
pass
class Meta:
model = Comment
fields = ['text']
HTML
<form action="{% url 'comments:create' pk=post.pk %}" method="POST" >
{% csrf_token %}
<div class="input-group">
<input type="text" class="form-control" placeholder="Comment here" aria-label="text" name="text" value="{{ post.comment.pk}}" >
<div class="input-group-append">
<button class="btn btn-dark" type="submit" id="button-addon2"><i class="fas fa-comment"></i></button>
</div>
</div>
</form>
You should link the .user attribute to the logged in user, so:
from django.contrib.auth.decorators import login_required
#login_required
def create_comment(request, pk):
if request.method == 'POST':
form = CommentForm(request.POST, request.FILES)
if form.is_valid():
form.instance.post_id = pk
form.instance.user = request.user
form.save()
return redirect('posts:feed')
else:
form = CommentForm()
return render(request, 'posts/feed.html', {'form': form})
By making use of .post_id = pk, we no longer query the database to obtain the post first, validation is done when creating the Comment instance.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

Link Username To Post Created By User

I would like to link the user who created the post by their username that way people know who it was posted by but I can't seem to get it to work and yes I am logged in and I have a working register and login form already.
Every time I go to submit some news from the form when logged in I get this error NOT NULL constraint failed: news_news.author_id
models.py
from django.db import models
from django.contrib.auth.models import User
from markdownx.models import MarkdownxField
from markdownx.utils import markdownify
from taggit.managers import TaggableManager
class News(models.Model):
author = models.ForeignKey(User, unique=True, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
short_desc = models.CharField(max_length=500)
content = MarkdownxField()
tags = TaggableManager()
slug = models.SlugField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
views.py
from django.shortcuts import render, redirect, get_object_or_404
from .models import Category, News
from .forms import NewNewsForm
from django.shortcuts import render
def show_news_view(request):
news = News.objects.values('author', 'title', 'short_desc', 'tags', 'created_at', 'updated_at')
context = {
'news': news
}
return render(request, "news/news_home.html", context)
def new_news_form_view(request):
if request.method == "POST":
form = NewNewsForm(request.POST or None)
if form.is_valid():
form.save()
form = NewNewsForm()
return redirect('/news')
else:
form = NewNewsForm()
context = {
'form': form
}
return render(request, "news/news_form.html", context)
EDIT:
forms.py
from django import forms
from .models import News
class NewNewsForm(forms.ModelForm):
class Meta:
model = News
fields = ['title', 'short_desc', 'content', 'category', 'tags', 'slug']
news_form.html
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<form action="" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
{% else %}
<p>Please login before you can submit a news story.</p>
{% endif %}
{% endblock content %}
You are not passing the user.
Later edit with a simpler solution:
Simply pass commit=False when saving the form. Which will create your News object without commiting it to DB. Simply set the author afterwards and save the object.
def new_news_form_view(request):
if request.method == "POST":
form_data = request.POST or None
form = NewNewsForm(form_data)
if form.is_valid():
news = form.save(commit=False)
news.author = request.user
news.save()
return redirect('/news')
else:
form = NewNewsForm()
context = {
'form': form
}
return render(request, "news/news_form.html", context)

How to autofill editable forms in Django

tl;dr How to autofill an editable form with information stored in database
Hey, Im creating a profile page for an application using Django as a framework. And Im having some annoying issues when a user is editing their page. As it is now, the user has to retype every field in the form, to edit a single field.. Cause my view has to delete the previous information in each field, or I get some annoying errors.
So my question is, is there a way to autofill these fields in profile_edit.html with the strings corresponding to each field in the form, from the database?
Any help would be greatly appreciated :D
view.py
#login_required
def profile_edit(request):
form = ProfileUpdateForm(request.POST, request.FILES)
if request.method == 'POST':
if form.is_valid():
user = request.user
if 'image' in request.FILES:
user.profile.image = request.FILES['image']
user.profile.bio = form.cleaned_data.get("bio")
user.profile.birth_date = form.cleaned_data.get("birth_date")
user.profile.location = form.cleaned_data.get("location")
user.save()
return redirect('profile')
else:
form = ProfileUpdateForm()
context = {
'form' : form
}
return render(request, 'webside/profile_edit.html', context)
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
email_confirmed = models.BooleanField(default=False)
image= models.FileField(upload_to='profile_image/', blank = True)
def __str__(self):
return self.user.username
profile_edit.html
'{% csrf_token %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field }}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}'
pic of profile.html
forms.py
class ProfileUpdateForm(forms.ModelForm):
YEARS= [x for x in range(1900,2021)]
birth_date = forms.DateField( initial="21-06-1995", widget=forms.SelectDateWidget(years=YEARS))
class Meta:
model = Profile
fields = ('bio','birth_date','location','image')
The way you initialise your form in your view is all wrong:
def profile_edit(request):
user = request.user
# form = ProfileUpdateForm(request.POST, request.FILES) <-- remove
if request.method == 'POST':
form = ProfileUpdateForm(request.POST, request.FILES, instance=user.profile)
if form.is_valid():
form.save() # <-- you can just save the form, it will save the profile
# user.save() <-- this doesn't help you, it doesn't save the profile and since user isn't changed you don't need to save it!
return redirect(...)
# else:
# form = ProfileUpdateForm() <-- don't clear the form!
else: # GET
form = ProfileUpdateForm(instance=user.profile) <-- initialise with instance
context = {
'form' : form
}
return render(request, 'webside/profile_edit.html', context)
You need to add the instance to the form to update an existing instance. You shouldn't initialise an empty form if the form is not valid, because that means the user loses all the data if they made a mistake. You want to display the form with all the data and the errors in that case.

Django forms not working

Hi I have a simple django form, which enables the users to signup to the website. but am confused how can I submit my form fields. Am new to Django. Please help me on it. Thanks in advance.
Forms.py:
from django import forms
from django import forms
from django.contrib.auth.models import User # fill in custom user info then save it
# from django.contrib.auth.forms import UserCreationForm
class UserForm(forms.Form):
email = forms.EmailField(max_length=100, null=True, blank=False)
first_name = forms.CharField(max_length=20)
password = forms.CharField(max_length=20, required=True, label="password", widget=forms.PasswordInput)
last_name = forms.CharField(max_length=20)
date_joined = forms.DateTimeField(auto_now_add=True, auto_now=False)
date_ = forms.DateTimeField(auto_now_add=False, auto_now=True)
Views.py
def register_user(request):
if request.method == 'POST':
print "Saisisis"
form = UserForm(request.POST) # create form object
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success')
print "blah"
args = {}
args.update(csrf(request))
args['form'] = UserForm()
# import pdb
# pdb.set_trace()
print args
return render(request, 'pages/signup.html', args)
and my html:
{% extends 'pages/base.html' %}
{% block additional_styles %}
<style>
body{
background:url(static/img/nose.jpg) no-repeat center center fixed;
-webkit-background-size: cover
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
</style>
{% endblock %}
{% block contentblock %}
<div class="container well">
<h1> Please Sign Up fellas</h1>
<form method="POST" action="login.html">
{% csrf_token %}
{{ form.as_table }}
<input type="submit" value="OK">
</form>
</div>
{% endblock %}
To do what you've got there, you'd need to have a ModelForm so that when you call form.save() Django knows what the model you are creating an instance of. For example;
Forms.py:
from django import forms
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
email = forms.EmailField(max_length=100, null=True, blank=False)
first_name = forms.CharField(max_length=20)
password = forms.CharField(max_length=20, required=True, label="password", widget=forms.PasswordInput)
last_name = forms.CharField(max_length=20)
date_joined = forms.DateTimeField(auto_now_add=True, auto_now=False)
date_ = forms.DateTimeField(auto_now_add=False, auto_now=True)
class Meta:
model = User
But going from what you've got you'd need to create the model instance yourself, then set the data, then save it;
def register_user(request):
if request.method == 'POST':
form = UserForm(request.POST) # create form object
if form.is_valid():
email = form.cleaned_data['email']
user = User(email=email)
user.save()
return HttpResponseRedirect('/accounts/register_success')

Categories

Resources