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)
Related
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 }}
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].
I am very beginner in django. I want to create a post form which be able to have title, content, image/file upload and etc.
I am very confused with concept of modelforms and createview. I tried this:
blog/view.py:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content', 'imagefile']
success_url = '/blog/home/'
# template_name = 'blog/post_form.html'
def __init__(self, *args, **kwargs):
super(PostCreateView, self).__init__(**kwargs) # Call to ModelForm constructor
def form_valid(self, form):
form.instance.author = self.request.user
form.save()
return super().form_valid(form)
blog/templates/blog/post_form.html:
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Post::</legend>
{{ form|crispy }}
<img src='{{ post.imagefile.url }}'>
<br><br>
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Update</button>
</div>
</form>
</div>
{% endblock content %}
blog/urls.py:
from django.urls import path
from .views import (
PostCreateView,
)
urlpatterns = [
path('blog/post/new/', PostCreateView.as_view(), name='post-create')
]
blog/models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
# image = models.ImageField(default='default_post.jpg', upload_to='postimages')
imagefile = models.FileField(upload_to='postimages', null=True, verbose_name="")
# if user is deleted the idea should be deleted as
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
# return self.title
return self.title + ": " + str(self.imagefile)
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
My question: All I want is make a kind of form to create the new post with title, content, upload button and submit button. However I don't know if CreateView can be customised even for adding further items or forms.Modelforms should be used?
You would want your view to be in views.py and your form to be in forms.py. You will need both, not one or the other.
Something like:
class CreateXYZView(CreateView):
template_name = "blog/post_form.html"
model = Post
form_class = postform
...do view stuff
def post(self, request, *args, **kwargs):
Also have a forms.py file
class postform(forms.ModelForm):
class Meta:
model = Post
widgets = {}
exclude = ['fieldname']
It looks like you are using a model named Post which is a good idea. By declaring postform as a forms.ModelForm it will pull your model fields into the form I.E from class Post it pulls title, content, imagefield, etc.. unless the field is specifically added to the exclude parameter. This is useful for parameters in your model like auto fields created_by or post_date where the user should not fill these in manually. Add these to exclude so they do not show in the form.
You can also manually add form fields in your template after {{form|crispy}} but I would avoid that as it creates more work in processing the data.
If you are filling out a form that is not tied to a model you can also use forms.Form:
class SupportTicket(forms.Form):
title = forms.CharField(label="Titlte", max_length=250, widget=forms.TextInput(...)
content = forms....
E.G. use this where the information was being passed directly to GitLab and not saved locally into a model for use later.
I am new to Django and i am working on a website where user can submit a post. Django Form is not saving in database when i have manytomany field in model. I do not know if i can achieve this in Django, I want to attached other user names to the post so that when i submit the form the user name is selected automatically when i check on the post in admin. I will attach a screenshots for clarity.
This image below is my form, as you can see 'My post' is the image_caption while 'andy' is another user name, i want 'andy' selected automatically in manytomany field when form is submitted.
This is what i want when the form is submitted then i check in admin. The other user name (andy) is selected in manytomany field when the form is submitted. I did this manually
Model:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
profile_pic = models.ImageField(upload_to='ProfilePicture/', default="ProfilePicture/user-img.png", blank=True)
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
image_caption = models.TextField(blank=True, null=True)
tag_someone = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='tagged_users', blank=True)
Forms:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = (
'image_caption',
'tag_someone',
)
Views:
def upload_view(request):
ImageFormset = modelformset_factory(File, fields=('files',), extra=20)
if request.method == "POST":
form = PostForm(request.POST)
formset = ImageFormset(request.POST, request.FILES)
if form.is_valid() and formset.is_valid():
post = form.save(commit=False)
post.poster_profile = request.user
post.save()
form.save_m2m()
for f in formset:
try:
photo = File(post=post, files=f.cleaned_data['files'])
photo.save()
except Exception as e:
break
return redirect('/')
else:
form = PostForm()
formset = ImageFormset(queryset=File.objects.none())
#User Name Auto-complete In Tag Form
all_users = User.objects.values_list('username', flat=True)
context = {
'form': form,
'formset': formset,
'all_users': all_users,
}
return render(request, 'upload.html', context)
Upload.html:
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
{{ formset }}
<button type="submit" class="btn btn-primary btn-sm btn-block w-25">Post</button>
</form>
I was able to get this working by changing widget to:
widget = {
forms.Select(),
}
You can change the widget here. Widgets are the thing that is responsible for outputting the inputs to HTML. So you just need:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = (
'image_caption',
'tag_someone',
)
widgets = {'tag_someone': forms.TextInput}
I was able to load the comments that were added through the admin page but I am not able to make a form in the DetailView itself
I have tried adding a form in the detailview template but I still don't see the form in the site
#views.py
class MessageDetailView(DetailView):
model = Message
template_name = "messaging/detail.html"
#queryset = Message.objects.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.filter(message=self.object)
return context
#detail.html
<form method="POST">
{% csrf_token %}
<h3>Write a New Comment</h3>
<div class="messagebox">
{{ form|crispy }}
<button class="btn" type="submit">
Post Comment
</button>
</div>
</form>
#forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ("comment")
#models.py
class Comment(models.Model):
message = models.ForeignKey(Message,on_delete=models.CASCADE)
comment = models.TextField(max_length=50)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return "Comment on {}".format(str(self.date_posted))
The comments loaded in the site, but the form didn't load, any way to solve this problem? Please provide some code in the answer instead of just linking me to a documentary.
you didn't pass the form to your template:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.filter(message=self.object)
context['form'] = CommentForm()
return context