I have a problem with CreateView. My code is a bit of a frankenstein monster with code from various tutorials, docs, and stackoverflow. I feel like I have misunderstood some fundamental step in the workflow.
Here is the models.py:
class Customer(models.Model):
name = models.CharField(max_length=200, null=False, blank=False)
phone = models.CharField(max_length=50, null=False, blank=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='customers')
Here is the forms.py:
class CustomerForm(forms.ModelForm):
def clean(self):
super().clean()
name = form.cleaned_data['name'].upper()
form.cleaned_data['name'] = name
class Meta:
model = Customer
fields = ['name', 'phone']
widgets = {
'name': forms.TextInput(attrs={"class": "form-control"}),
'phone': forms.TextInput(attrs={"class": "form-control"}),}
Here is the views.py:
class CustomerCreateView(LoginRequiredMixin, CreateView):
model = Customer
form_class = CustomerForm
context_object_name = 'customer_create'
template_name = 'customers/customer-create.html'
login_url = 'account_login'
def form_valid(self, form):
form.instance.created_by = self.request.user
return super().form_valid(form)
And lastly here is the template:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
The problem is that when I hit save the page just refreshes and the new object is not created.
What am I doing wrong?
UPDATE:
changing form_valid method to this fixed the problem:
def form_valid(self, form):
form.instance = form.save(commit=False)
form.instance.created_by = self.request.user
form.instance.save()
return super().form_valid(form)
In forms.py
fields = ['name', 'phone']
should be replaced by
fields = ('name', 'phone')
Related
I have django application where I want user to be able to upload videos. My view looks like this:
class CreateVideo(View):
def post(self, request):
videos = models.Video.objects.all().order_by('-created_on')
form = forms.VideoUploadForm(request.POST)
if form.is_valid():
print('form is valid')
video = form.save(commit=False)
video.save()
print('video uploaded')
else:
print('form not valid')
context = {
'video_list': videos,
'form': form,
}
return redirect('index')
def get(self, request):
videos = models.Video.objects.all().order_by('-created_on')
form = forms.VideoUploadForm()
context = {
'video_list': videos,
'form': form,
}
return render(request, 'videos/upload_video.html', context)
My form:
class VideoUploadForm(forms.ModelForm):
class Meta:
model = Video
fields = ['title', 'description', 'file']
and model:
class Video(models.Model):
video_id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False,
unique=True
)
title = models.CharField(max_length=50, null=True)
description = models.CharField(max_length=500, null=True)
file = models.FileField(null=True)
created_on = models.DateTimeField(default=timezone.now, null=True)
at = models.ForeignKey(at, on_delete=models.CASCADE, null=True)
and my template:
<div>
<form method="post">
{% csrf_token %}
{{ form | crispy }}
<button>Submit!</button>
</form>
</div>
When I click submit button, I get: form not valid in terminal. I want form to be created, but form is just never valid. Where is the problem?
Are you setting the title, description and file fields when submitting the form? For now, they're considered required by your form until you set blank=True on the model's fields (you can read more about it here: https://stackoverflow.com/a/8609425/7196167).
I am using Django 3.2.6
class FeedbackForm(CreatedMixin,
CommentMixin,
FlagMixin,
models.Model):
name = models.CharField(blank=False,
default="",
null=False,
unique=False,
max_length=500,
verbose_name=gettext("Name:"))
email = models.EmailField(blank=True,
default="",
null=False,
unique=False,
verbose_name=gettext("Email:"))
phone = PhoneField(blank=True,
default="",
null=False,
unique=False,
verbose_name=gettext("Phone:"))
message = models.TextField(blank=False,
default="",
null=False,
verbose_name=gettext("Message:"))
def __str__(self):
return f"{self.name}_{self.email}_{self.phone}"
class FeedbackForm(ModelForm):
class Meta:
exclude = [OmnibusNames.COMMENT.value,
OmnibusNames.FLAG.value,
OmnibusNames.CREATED.value,]
model = FeedbackForm
class FeedbackFormView(FormView):
template_name = 'feedback_forms/feedback_form.html'
form_class = FeedbackForm
class FeedbackFormResult(View):
def post(self, request, *args, **kwargs):
feedback_form_model = apps.get_model(app_label="feedback_forms", model_name="FeedbackForm")
new_feedback = feedback_form_model(request.POST)
new_feedback.save()
return render(request, template_name="feedback_forms/feedback_form.html")
<form id="subscription-form" onsubmit="return false">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
The problem:
{TypeError}Field 'id' expected a number but got <QueryDict: {'csrfmiddlewaretoken': ['W8wfxQvsmQyElPtsdmJFiDJmzbyKZXRYfZS9TAbFGpNZDo22jWaLotUrllHYsDWi'], 'name': ['John'], 'email': ['tad#gmail.com'], 'phone_0': ['+19617828635'], 'phone_1': [''], 'message': ['ddd']}>
Something with id. But I thought that Id is auto-incremented. What can I try to resolve this?
Only a form can process the request.POST, so you should not create a model with request.POST but a form:
from app_name.forms import FeedbackForm
from django.shortcuts import redirect, render
class FeedbackFormResult(View):
def post(self, request, *args, **kwargs):
form = FeedbackForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('name-of-some-view')
return render(request, 'name-of-some-template.html', {'form': form})
I would strongly advise not to give a model and form the same name, that makes not much sense anyway.
Furthermore it might make more sense to work with a FormView [Django-doc] which will implement most of the boilerplate logic.
models.py
class Contents(models.Model):
name = models.CharField(max_length=100)
formula = models.CharField(max_length=20)
description = models.TextField(null=True, max_length=100)
wiki = models.CharField(null=True, max_length=100)
youtube = models.CharField(null=True, max_length=100)
file = models.FileField()
def __str__(self):
return self.name
view.py
class ContentsListCreateAPIView(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'admin3.html'
style = {'template_pack': 'rest_framework/vertical/'}
def get(self, request):
queryset = Contents.objects.all()
serializer = ContentsSerializer
return Response({'serializer': serializer, 'contents': queryset, 'style': self.style})
def post(self, request):
queryset = Contents.objects.all()
serializer = ContentsSerializer(data=request.data)
print(serializer.is_valid())
print(serializer.data)
if serializer.is_valid():
serializer.save()
return redirect('contents_list')
return redirect('contents_list')
serializers.py
from rest_framework import serializers
from .models import Contents
class ContentsSerializer(serializers.ModelSerializer):
class Meta:
model = Contents
fields = '__all__'
admin.html
<form action="{% url 'contents_list' %}" method="POST">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save">
It seems that the filefield does not get the proper input. I don't get what the problem is. The CreateAPIView works well when I use the default DRF template.
I started learning Django about a month ago and just finished Django for Beginners by William Vincent. The book ends with Ch.15: Comments and shows how the admin can add comments to posts.
Q: Can someone, please, show me or point me in the right direction as to how I can let registered users also add comments to posts? Is there perhaps a 3rd party app for that?
What I have so far:
Models:
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='images/', null=True, blank=True, height_field=None, width_field=None)
upload = models.FileField(upload_to='files/', null=True, blank=True)
author = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('article_detail', args=[str(self.id)])
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comment')
comment = models.CharField(max_length=140)
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
get_user_model(), on_delete=models.CASCADE,
)
def __str__(self):
return self.comment
def get_absolute_url(self):
return reverse('article_list')
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Article
fields = ('title', 'body', 'image', 'upload')
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('comment', 'author')
Views:
class ArticleListView(LoginRequiredMixin, ListView):
model = Article
template_name = 'article_list.html'
comment_form = CommentForm
login_url = 'login'
class ArticleDetailView(LoginRequiredMixin, DetailView):
model = Article
template_name = 'article_detail.html'
login_url = 'login'
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ('title', 'body', 'image', 'upload')
template_name = 'article_edit.html'
login_url = 'login'
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if obj.author != self.request.user:
raise PermissionDenied
return super().dispatch (request, *args, **kwargs)
class ArticleDeleteView(LoginRequiredMixin, DeleteView):
model = Article
template_name = 'article_delete.html'
success_url = reverse_lazy('article_list')
login_url = 'login'
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if obj.author != self.request.user:
raise PermissionDenied
return super().dispatch (request, *args, **kwargs)
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
form_class = PostForm
template_name = 'article_new.html'
login_url = 'login'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
URLs:
urlpatterns = [
path('<int:pk>/edit/', ArticleUpdateView.as_view(), name='article_edit'),
path('<int:pk>/', ArticleDetailView.as_view(), name='article_detail'),
path('<int:pk>/delete/', ArticleDeleteView.as_view(), name='article_delete'),
path('', ArticleListView.as_view(), name='article_list'),
path('new/', ArticleCreateView.as_view(), name='article_new'),]
Thank you for your attention.
Solved. In my views.py I added the following function:
def add_comment(request, pk):
article = get_object_or_404(Article, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.article = article
comment.save()
return redirect('article_detail', pk=article.pk)
else:
form = CommentForm()
return render(request, 'add_comment.html', {'form': form})
Then the following .html file was added to templates:
add_comment.html
{% extends 'base.html' %}
{% block content %}
<h4>Add a Comment</h4>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.as_p }}</p>
<button type="submit" class="btn btn-success">Submit</button>
</form>
{% endblock content %}
P.S.: Initially I was getting an ImportError: cannot import name 'add_comment' from 'articles.views'.
I thought it was a circular import problem and what worked for me was just getting the def add_comment indentation right.
I'm new to coding with django, and I'm trying to add comments to my blog app, but I'm having trouble with the validation of this form, it always returns False with form.is_valid(), so the object is never saved
views.py
def blog_post_detail_view(request, slug):
obj = get_object_or_404(BlogPost, slug=slug)
comments = Comment.objects.filter(blog_post=obj)
initial_data = {
"blog_post": obj,
}
form = CommentModelForm(request.POST or None, initial=initial_data)
if form.is_valid():
comment_obj = form.save(commit=False)
comment_obj.user = request.user
comment_obj.save()
form = CommentModelForm()
else:
print('not clean')
context = {
"object": obj,
"comments": comments,
"form": form,
}
template_name = 'blog/detail.html'
return render(request, template_name, context)
forms.py
from django import forms
from .models import Comment
class CommentModelForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content','blog_post']
HTML
<form method='POST' action='.'> {% csrf_token %}
{{ form.as_p }}
<button type='submit'>Send</button>
</form>
models.py
class Comment(models.Model):
content = models.TextField(max_length=300)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL, default=1)
blog_post = models.ForeignKey(BlogPost, null=True, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return str(self.user.username)
def __str__(self):
return str(self.user.username)