Generic detail view needs object pk or a slug - django

But I already refer to primary keys, don't I?
It says this error relates to:
class CommentUpdate(UpdateView):
model = Comment
fields = ['body']
def form_valid(self, form):
film = Film.objects.get(pk=self.kwargs['film_id'])
comment = Film.objects.get(pk=self.kwargs['comment_id'])
form.instance.user = self.request.user
form.instance.film = film
form.instance.comment = comment
return super(CommentUpdate, self).form_valid(form)
I am not sure once this issue is fixed if that code above will work but the view I have to create a comment does:
class CommentCreate(CreateView):
model = Comment
fields = ['body']
def form_valid(self, form):
film = Film.objects.get(pk=self.kwargs['film_id'])
form.instance.user = self.request.user
form.instance.film = film
return super(CommentCreate, self).form_valid(form)
My urls.py:
path('<int:film_id>/comment/', views.CommentCreate.as_view(), name='add_comment'),
path('<int:film_id>/comment/<int:comment_id>/', views.CommentUpdate.as_view(), name='update_comment'),
model:
class Comment(models.Model):
# user = models.ForeignKey(User, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
film = models.ForeignKey(Film, on_delete=models.CASCADE)
body = models.CharField(max_length=200)
def get_absolute_url(self):
return reverse('films:detail', kwargs={'pk': self.film.pk})
And html link I have:
Leave a comment
Update

UpdateView calling get_object method which required pk or slug url argument to get updating object. You can change name of argument with pk_url_kwarg:
class CommentUpdate(UpdateView):
model = Comment
fields = ['body']
pk_url_kwarg = 'comment_id'

Related

Setting form fields in django class based generic view CreateView

I'm using django's CreateView to add images to a book. I pass the book's id to the class based view as a parameter in the url. Form fields such as book and language are not rendered on the template, rather they're obtained with the help of the book's id.
# views.py
class PictureCreateView(CreateView):
model = Upload
fields = "__all__"
book_id = None
def get_initial(self):
initial = super(PictureCreateView, self).get_initial()
initial = initial.copy()
self.book_id = self.kwargs['book_id']
book = Book.objects.get(id=self.book_id)
initial['book'] = book
initial['language'] = language
initial['uploader'] = self.request.user
return initial
# set book_id so it used in the template
def get_context_data(self, **kwargs):
context = super(PictureCreateView, self).get_context_data(**kwargs)
context['book_id'] = self.book_id
return context
def form_valid(self, form, **kwargs):
print('Form is valid')
self.object = form.save()
files = [serialize(self.object)]
data = {'files': files}
response = JSONResponse(data, mimetype=response_mimetype(self.request))
response['Content-Disposition'] = 'inline; filename=files.json'
return super(PictureCreateView, self).form_valid(form)
def form_invalid(self, form):
print('Form invalid!')
print(form.errors)
data = json.dumps(form.errors)
return HttpResponse(content=data, status=400, content_type='application/json')
# models.py
class Upload(models.Model):
image = models.ImageField(upload_to=get_upload_path, help_text='Image to process')
uploader = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, related_name='uploader')
language = models.ForeignKey(Language, models.CASCADE)
book = models.ForeignKey(Book, models.CASCADE)
The problem is that I get an error saying the form is invalid, and the fields uploader, book and language are required. How do I resolve this?
The initial data is used to display the defaults when the form is initially displayed. It isn't used when those values are missing from the submitted form data. If fields like book and uploader are set from the URL or logged-in user, then you should leave them out of the form completely, instead of setting them in the initial data. You can then set the values on the instance in the form_valid method before the form is saved.
from django.contrib.auth.mixins import LoginRequiredMixin
class PictureCreateView(LoginRequiredMixin, CreateView):
model = Upload
fields = ['other_field1', 'other_field2', ...] # leave out book, language and uploader
def form_valid(self, form):
self.book_id = self.kwargs['book_id']
book = Book.objects.get(id=self.book_id)
form.instance.book = book
form.instance.language = ????
form.instance.uploader = self.request.user
return super(
The LoginRequiredMixin makes sure that only logged-in users can access the view.
You may want to use get_object_or_404 to handle the case where book_id refers to a book that does not exist.
One thought, initial doesn't fill the model for submission. You need to do that in init
def __init__(self):
super(PictureCreateView, self).__init__()
self.fields['book'] = self.initial['book']
self.fields['uploader'] = self.initial['uploader']
self.fields['language'] = self.initial['book']
Or, if you don't want to set the fields, make sure they are optional in your original model:
class Upload(models.Model):
uploader = models.ForeignKey('uploader', on_delete=models.CASCADE, null=True, blank=True)
book = models.ForeignKey('book', on_delete=models.CASCADE, null=True, blank=True)
language = models.ForeignKey('language', on_delete=models.CASCADE, null=True, blank=True)

getting error while relating a foreign key to save form in django

models.py
class OtherData(models.Model):
title = models.CharField(max_length=120)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
class ProductImage(models.Model):
otherdata = models.ForeignKey(OtherData)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
image = models.FileField(blank=True, null=True, upload_to='images/')
I am looking for saving an image on an instance of otherdata, getting integrity error NOT NULL constraint failed. I am using a model form to save data. I tried to use form valid method as follows in views.py but still the same error.
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.otherdata_id = self.kwargs.get('pk')
return super(ImageCreateView, self).form_valid(form)
Looking forward for a help, thank you.
can you try this,you can get OtherData instance from pk
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.otherdata = OtherData.objects.get(pk=self.kwargs.get('pk'))
return super(ImageCreateView, self).form_valid(form)
and also small changes in model.py
class OtherData(models.Model):
title = models.CharField(max_length=120)
user = models.ForeignKey(settings.AUTH_USER_MODEL,related_name='other_data')
class ProductImage(models.Model):
otherdata = models.ForeignKey(OtherData)
user = models.ForeignKey(settings.AUTH_USER_MODEL,related_name='product_image')
image = models.FileField(blank=True, null=True, upload_to='images/')

Need help getting correct instance for form_valid in a generic view

I can't work out how to get the correct instance for the form_valid part of my generic view.
I am trying to allow a user to post on their project wall(bit like Facebook). I need the post to be related to an individual project(a user can have more than one project). Should the instance be a pk or the project title? Any example code or help would be very appreciated! I struggle understanding how when you create a new post, it knows which project to associate itself with.
views
class NewPost(CreateView):
model = ProjectPost
form_class = ProjectPostForm
template_name = 'howdidu/new_post.html'
def form_valid(self, form):
newpost = form.save(commit=False)
form.instance.user = self.request.user
newpost.save()
self.object = newpost
return super(NewPost, self).form_valid(form)
def get_success_url(self):
project_username = self.request.user.username
project_slug = self.object.slug
return reverse('user_project', kwargs={'username':project_username, 'slug': project_slug})
models
class UserProject(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=100)
project_overview = models.CharField(max_length=1000)
project_picture = models.ImageField(upload_to='project_images', blank=True)
date_created = models.DateTimeField(auto_now_add=True)
project_views = models.IntegerField(default=0)
project_likes = models.IntegerField(default=0)
project_followers = models.IntegerField(default=0)
slug = models.SlugField(max_length=100, unique=True) #should this be unique or not?
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(UserProject, self).save(*args, **kwargs)
def __unicode__(self):
return self.title
class ProjectPost(models.Model):
project = models.ForeignKey(UserProject)
title = models.CharField(max_length=100)
post_overview = models.CharField(max_length=1000)
date_created = models.DateTimeField(auto_now_add=True)
post_views = models.IntegerField(default=0)
post_likes = models.IntegerField(default=0)
forms
#form to add project details
class UserProjectForm(forms.ModelForm):
class Meta:
model = UserProject
fields = ('title', 'project_picture', 'project_overview')
#form to create a post
class ProjectPostForm(forms.ModelForm):
class Meta:
model = ProjectPost
fields = ('title', 'post_overview')
Ok, in that case, I would recommend a URL something like
url(r'^(?P<pk>\d+)/post/add/$', views.NewPostCreateView.as_view(), name='...'),
and then a view like
class NewPost(CreateView):
model = ProjectPost
form_class = ProjectPostForm
template_name = 'howdidu/new_post.html'
def form_valid(self, form):
self.object = form.save(commit=False)
# Find project by using the 'pk' in the URL
project = get_object_or_404(UserProject, pk=self.kwargs['pk'])
# Then just set the project on the newPost and save()
self.object.project = project
self.object.save()
return super(NewPost, self).form_valid(form)
def get_success_url(self):
# Unchanged ...
I see in your code that you were trying to do something with the user but I don't understand why your Post does not have a user field (you may want to add a created_by) and the UserProject should already have a user set.
I am also assuming the user got to the his/her project first, so you know by definition that the project he is adding a post to is his. If that is not the case, then just change the logic to get the UserProject through a regular query. e.g. maybe with `UserProject.objects.get(user = self.request.user) if there is one project per user (again, just as an example).
Anyway, I am making some assumptions here, but hopefully the main question was how to set the project on the newPost and that is answered in my example.

how to save many to many fields in django create view

Here is my models:
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=255, blank=True,default=None)
desc = models.TextField(blank=True, null=True )
.....
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
categories = models.ManyToManyField(Category, blank=True, through='CatToPost')
.......
class CatToPost(models.Model):
post = models.ForeignKey(Post)
category = models.ForeignKey(Category)
The problem now I can't make it work to save the many-to-many field by using the generic create view.
Cannot set values on a ManyToManyField which specifies an intermediary
model. Use posts.CatToPost's Manager instead.
In SO there was a similar problem that suggest override the form_valid method to manually create the relation, but it didn't works for me.
def form_valid(self, form):
self.object = form.save(commit=False)
for cat in form.cleaned_data['categories']:
cate = CatToPost()
cate.post = self.object
cate.category = cat
cate.save()
return super(AddStoryForm, self).form_valid(form)
The error:
Cannot assign "": "Post" instance isn't saved
in the database.
Seem self.object = form.save(commit=False) not saving in the db, so the Post ID wasn't created.
But when I turn self.object = form.save(commit=True) , I still got the previous error occurred again.
Any idea how can I overcome this problem?
I also had a similar problem to the answer you listed. For me what worked is to add self.object.save() after self.object = form.save(commit=False)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.save()
for cat in form.cleaned_data['categories']:
cate = CatToPost()
cate.post = self.object
cate.category = cat
cate.save()
return super(AddStoryForm, self).form_valid(form)

Filter select field in ModelForm by currently logged in user

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .