How to use detailview pk in createview - django

# models.py
class NewBlank(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
title = models.CharField(max_length=50)
description = models.CharField(max_length=100, blank=True)
blank_on_off = models.BooleanField(default=False)
create_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True)
class BlankContent(models.Model):
refer = models.TextField()
memo = models.TextField()
new_blank = models.ForeignKey('NewBlank', on_delete=models.CASCADE, related_name='blankcontent')
# views.py
class BlankDetail(LoginRequiredMixin, DetailView):
model = NewBlank
template_name = 'blank_app/blank_detail.html'
context_object_name = 'blank'
class BlankContentCreate(CreateView):
model = BlankContent
fields = "__all__"
template_name = 'blank_app/new_blank_content_create.html'
def get_success_url(self):
return reverse_lazy('blank_detail', kwargs={'pk': self.object.new_blank.pk})
# urls.py
urlpatterns = [
path('blank/<int:pk>/', BlankDetail.as_view(), name='blank_detail'),
path('new-blank-content/', BlankContentCreate.as_view(), name='blank_content_create'),
]
There is a creativeview in the detail view and I want to create a model in the detailview when I press it. So even if I don't specify the new_blank part, I want it to be filled automatically according to the pk in the detailview, what should I do?

In case you want to perform some extra work in your DetailView, one of the ways to do that would be to override the get_object method.
from django.views.generic import DetailView
class BlankDetail(LoginRequiredMixin, DetailView):
model = NewBlank
template_name = 'blank_app/blank_detail.html'
context_object_name = 'blank'
def get_object(self):
obj = super().get_object()
# do your thing with obj.pk
pk = self.kwargs.get('pk') # in case you want to access the `pk` from URL

Related

Django Modelform user issues

I have a model form to list an item and I am trying to get the form to fill in the user id from the user that is submitting the form. Currently, the form is submitted successfully but it always uses the first user in the database's id for every item.
models.py
class Item(models.Model):
id = models.UUIDField( primary_key=True, default=uuid.uuid4, editable=False )
creator = models.ForeignKey( get_user_model(), on_delete=models.CASCADE, default=2)
item_name = models.CharField(max_length=40)
price = models.DecimalField(max_digits = 6, decimal_places=2)
description = models.CharField(max_length= 500)
main_image = models.ImageField(upload_to=path_and_rename , max_length=255, null=True, blank=True)
image_2 = models.ImageField(upload_to='items/', blank=True)
image_3= models.ImageField(upload_to='items/', blank=True)
image_4= models.ImageField(upload_to='items/', blank=True)
image_5= models.ImageField(upload_to='items/', blank=True)
quantity = models.IntegerField(default=1, validators=[ MaxValueValidator(100),MinValueValidator(1)])
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
def __str__(self):
return self.item_name
def get_absolute_url(self):
return reverse("item_detail_view", args=[str(self.id)])
forms.py
from django.forms import ModelForm, forms
from .models import Item
class List_Item_Form(ModelForm):
forms.ModelChoiceField(queryset=Item.objects.filter(user=user))
class Meta:
model = Item
def __init__(self, *args, **kwargs):
user = kwargs.pop("user", None)
super().__init__(*args, **kwargs)
views.py
class AddListing( generic.CreateView):
template_name = 'store/add_listing.html'
fields = ('item_name','price','description','main_image','quantity')
model = Item
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
It can be done using function based view too because there we get current user more easily.
Try this:
models.py
from django.db import models
from django.contrib.auth.models import User
class Item(models.Model):
creator = models.ForeignKey(User, on_delete=models.CASCADE)
item_name = models.CharField(max_length=40)
price = models.DecimalField(max_digits=6, decimal_places=2)
description = models.CharField(max_length=500)
main_image = models.ImageField(
upload_to=path_and_rename, max_length=255, null=True, blank=True)
image_2 = models.ImageField(upload_to='items/', blank=True)
image_3 = models.ImageField(upload_to='items/', blank=True)
image_4 = models.ImageField(upload_to='items/', blank=True)
image_5 = models.ImageField(upload_to='items/', blank=True)
quantity = models.PositiveIntegerField(default=1)
created_on = models.DateTimeField(auto_now_add=True)
updated_on = models.DateTimeField(auto_now=True)
def __str__(self):
return self.item_name
admin.py
from django.contrib import admin
from home.models import Item
#admin.register(Item)
class ItemRegister(admin.ModelAdmin):
lis_display = ['id', 'creator', 'item_name', 'price', 'description']
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.list, name='home'),
path('success/', views.success, name='success')
]
forms.py
from django.forms import ModelForm, forms
from .models import Item
class ListItemForm(ModelForm):
class Meta:
model = Item
fields = ['item_name', 'price', 'main_image'
'description', 'quantity']
views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .models import Item
from .forms import ListItemForm
def list(request):
if request.method == 'POST':
form = ListItemForm(request.POST)
if form.is_valid():
itemName = form.cleaned_data['item_name']
price = form.cleaned_data['price']
desc = form.cleaned_data['description']
quan = form.cleaned_data['quantity']
main_img = form.cleaned_data['main_image']
current_user = request.user
model_instance = Item(creator=current_user, item_name=itemName, price=price,
description=desc, quantity=quan, main_image=main_img)
model_instance.save()
return HttpResponseRedirect('/success/')
else:
form = ListItemForm()
return render(request, 'store/add_listing.html', {'form': form})
def success(request):
return render(request, 'store/success.html')
Rest of the fields of models you can customize very easily in the view.

Django Modelviewset Filtering

I have two models Category & Post. In Post model there is foreign key of category. Based on category I want to filter the data to show the post category wise. Here's my code.
models.py
class Category(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField()
parent = models.ForeignKey('self',blank=True, null=True ,related_name='news', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name_plural = "Category"
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' -> '.join(full_path[::-1])
class Post(models.Model):
NEWS_TYPE = (('Images','Images'),('Multi-Images','Multi-Images'),('Image-Text','Image-Text'),
('Audio-Video','Audio-Video'),('Audio-Video-Text','Audio-Video-Text'),('Audio','Audio'),
('Audio-Text','Audio-Text'))
POST_STATUS = (('Pending','Pending'),('Verified','Verified'),('Un-Verified','Un-Verified'),
('Published','Published'),('Mint','Mint'))
category = models.ForeignKey(Category, related_name='posts', on_delete=models.CASCADE)
post_type = models.CharField(max_length=100, verbose_name='Post Type', choices=NEWS_TYPE)
title = models.TextField(verbose_name='News Title')
content = models.TextField(verbose_name='News Content')
hash_tags = models.CharField(max_length=255, verbose_name='Hash Tags')
source = models.CharField(max_length=255, verbose_name='News Source')
author = models.ForeignKey(User, related_name='Post', on_delete=models.CASCADE)
views = models.ManyToManyField(User,related_name='Views', blank=True)
likes = models.ManyToManyField(User, related_name='Likes', blank=True)
dislikes = models.ManyToManyField(User, related_name='Dislikes', blank=True)
status = models.CharField(max_length=20, verbose_name='Status', choices=POST_STATUS, default='Pending')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return (self.post_type)+ '-' +self.title
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ('category','post_type','title','content','hash_tags','source','author','views',
'likes','dislikes','status')
views.py
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class PostAPI(viewsets.ModelViewSet):
serializer_class = PostSerializer
def get_queryset(self):
news_post = Post.objects.all()
return news_post
def retrieve(self, request, *args, **kwargs):
params = kwargs
print(params['pk'])
category = Category.objects.filter(name=params['pk'])
serializer = CategorySerializer(category, many=True)
return Response(serializer.data)
urls.py
from django.urls import path, include
from rest_framework import routers
from rest_framework.routers import DefaultRouter
from news.views import PostAPI, CategoryAPI
from . import views
router = DefaultRouter()
router.register('posts', views.PostAPI, basename='posts'),
router.register('category', views.CategoryAPI, basename='category'),
urlpatterns = router.urls
I tried solving in these way but it tells 'PostSerializer' object has no attribute 'get_category'. Is there anything i'm doing wrong. Please your support would be helpful. Thank you
I think then your approach should be the other way round, meaning you should add the list of Posts to your Category:
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('category','post_type','title','content','hash_tags','source','author','views',
'likes','dislikes','status')
class CategorySerializer(serializers.ModelSerializer):
posts = PostSerializer(many=True, read_only=True)
class Meta:
model = Category
fields = ['name', 'slug', 'parent', 'posts']
Attention: I changed the related name of your category field in the Post model to 'posts'
This should show you all Posts when retrieving a category. No need to override any method in your views:
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class PostAPI(viewsets.ModelViewSet):
queryset = Post.obejcts.all()
serializer_class = PostSerializer
If do not want identify the category by id but by category name, e.g.:
http://127.0.0.1:8000/news/category/sports/
add a custom lookup field to your category view, e.g.
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
lookup_field = 'name'
but make sure the lookup_field is unique

modelForm got an unexpected argument 'initial'

modelForm got an unexpected argument 'initial' I am getting this error. please can anyone explain how to solve this ?
Here is my model.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, default=None)
StudentID = models.CharField(max_length=8, blank=False, unique=True)
Branch = models.CharField(max_length=255,choices=Departments,default="CSE")
def __str__(self):
return f'{self.user.username} Profile'
class complaintForm(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
category = models.CharField(max_length=255,choices=complaints,default='Mess')
title = models.CharField(max_length=20)
content = models.CharField(max_length=100)
image = models.ImageField(upload_to='complaint_pics/')
def __str__(self):
return self.title
form.py
class complaintForm(forms.ModelForm):
class Meta:
model = complaintForm
fields = ['title','content','image',]
views.py
class CreateComplaintView(CreateView):
model = complaintForm
form_class = complaintForm
template_name = 'user/post_complaint.html'
success_url = 'success'
You passed your model to the form attribute. This is why ComplaintForm is not a good idea for a model name. You better rename this to Complaint for example:
class Complaint(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
category = models.CharField(max_length=255,choices=complaints,default='Mess')
title = models.CharField(max_length=20)
content = models.CharField(max_length=100)
image = models.ImageField(upload_to='complaint_pics/')
def __str__(self):
return self.title
You will need to construct and run migrations to rename the table at the database side.
Then you thus define your form as:
from app.models import Complaint
class ComplaintForm(forms.ModelForm):
class Meta:
model = Complaint
fields = ['title','content','image',]
Finally in your CreateView, you can use:
from app.models import Complaint
from app.forms import ComplaintForm
class CreateComplaintView(CreateView):
model = Complaint
form_class = ComplaintForm
template_name = 'user/post_complaint.html'
success_url = 'success'
def form_valid(self, form):
form.instance.user = self.request.user.profile
super().form_valid(form)
Note: normally a Django models, just like all classes in Python are given a name in PerlCase, not snake_case, so it should be: Complaint instead of complaint.

Save Django Form wizard data to three models with related fields

I am working on a project that requires use of form wizard to populate three related models. The first model - Listing - has general data which has a OneToOneField relationship with the second model (Property). The Listing model also has a many to many relationships with the third model (ListingImages). In general, I am using 4 forms in the wizard. Here is the models definition
models.py
class Listing(models.Model):
listing_type_choices = [('P', 'Property'), ('V', 'Vehicle'), ('B', 'Business/Service'), ('E', 'Events')]
listing_title = models.CharField(max_length=255)
listing_type = models.CharField(choices=listing_type_choices, max_length=1, default='P')
status = models.BooleanField(default=False)
featured = models.BooleanField(default=False)
city = models.CharField(max_length=255, blank=True)
location = PlainLocationField(based_fields=['city'], zoom=7, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
expires_on = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(User,
on_delete=models.CASCADE, editable=False, null=True, blank=True
)
listing_owner = models.ForeignKey(User,
on_delete=models.CASCADE, related_name='list_owner'
)
def __str__(self):
return self.listing_title
def get_image_filename(instance, filename):
title = instance.listing.listing_title
slug = slugify(title)
return "listings_pics/%s-%s" % (slug, filename)
class ListingImages(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
image_url = models.ImageField(upload_to=get_image_filename,
verbose_name='Listing Images')
main_image = models.BooleanField(default=False)
class Meta:
verbose_name_plural = "Listing Images"
def __str__(self):
return f'{self.listing.listing_title} Image'
class Property(models.Model):
sale_hire_choices = [('S', 'Sale'), ('R', 'Rent')]
fully_furnished_choices = [('Y', 'Yes'), ('N', 'No')]
listing = models.OneToOneField(Listing, on_delete=models.CASCADE)
sub_category = models.ForeignKey(PropertySubCategory, on_delete=models.CASCADE)
for_sale_rent = models.CharField(choices=sale_hire_choices, max_length=1, default=None)
bedrooms = models.PositiveIntegerField(default=0)
bathrooms = models.PositiveIntegerField(default=0)
rooms = models.PositiveIntegerField(default=0)
land_size = models.DecimalField(max_digits=10, decimal_places=2)
available_from = models.DateField()
car_spaces = models.PositiveIntegerField(default=0)
fully_furnished = models.CharField(choices=fully_furnished_choices, max_length=1, default=None)
desc = models.TextField()
property_features = models.ManyToManyField(PropertyFeatures)
price = models.DecimalField(max_digits=15, decimal_places=2)
currency = models.ForeignKey(Currency, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Here is the forms.py
from django import forms
from .models import Listing, Property, Vehicle, Business, ListingImages
from django.forms import modelformset_factory
class ListingDetails(forms.ModelForm):
class Meta:
model = Listing
fields = ['listing_title', 'city', 'location']
class PropertyDetails1(forms.ModelForm):
class Meta:
model = Property
fields = ['sub_category', 'for_sale_rent', 'bedrooms', 'bathrooms',
'rooms', 'land_size', 'available_from', 'car_spaces', 'fully_furnished',
'desc', 'currency', 'price'
]
class PropertyDetails2(forms.ModelForm):
class Meta:
model = Property
fields = ['property_features']
class ListingImagesForm(forms.ModelForm):
image_url = forms.ImageField(label='Listing Image',
widget=forms.ClearableFileInput(attrs={'multiple': True}),
required=False
)
class Meta:
model = ListingImages
fields = ['image_url']
ImageFormSet = modelformset_factory(ListingImages, form=ListingImagesForm, extra=3)
views.py
from django.shortcuts import render, redirect
import os
from .forms import ListingDetails, PropertyDetails1, PropertyDetails2, ListingImagesForm
from .models import ListingImages, Listing, Property
from formtools.wizard.views import SessionWizardView
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.forms import modelformset_factory
from django.contrib import messages
from django.http import HttpResponseRedirect, HttpResponse
from django.forms.models import construct_instance
class PropertyView(SessionWizardView):
# formset = ImageFormSet(queryset=Images.objects.none())
template_name = "listings/create_property.html"
form_list = [ListingDetails, PropertyDetails1, PropertyDetails2, ListingImagesForm]
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'media'))
def done(self, form_list, **kwargs):
listing_instance = Listing()
property_instance = Property()
listing_instance.created_by = self.request.user
listing_instance.listing_owner = self.request.user
listing_instance.listing_type = 'P'
for form in form_list:
listing_instance = construct_instance(form, listing_instance, form._meta.fields, form._meta.exclude)
property_instance = construct_instance(form, property_instance, form._meta.fields, form._meta.exclude)
listing = listing_instance.save()
property_instance.listing = listing
property_instance.save()
return HttpResponse('data saved successfully')
The problem that I am facing is that I am able to save the Listing model, but getting its primary id and using it to save the Property model is the problem. Again, the ListingImages model stores images related to the Listing model. How do I save these models to database considering that they are multiple?
What's wrong is that as described here, model.save() does not return the saved object, but None.
So the last few lines of the above code should be
listing_instance.save()
property_instance.listing = listing_instance
property_instance.save()
return HttpResponse('data saved successfully')
Ditto saving a set of listing_images would be something like
for li_obj in listing_image_instances:
li_obj.listing = listing_instance # saved above
li_obj.save()

Make Django Rest Api User Specific (get_queryset)

I want to make a Django rest api that is user specific so that I do /username at the end of the url.
Models:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.IntegerField()
description = models.CharField(max_length=300)
class Meta:
verbose_name_plural = 'User Profiles'
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_data(sender, update_fields, created, instance, **kwargs):
if created:
user = instance
profile = UserProfile.objects.create(user=user, age=18, description='No Description')
class Notes(models.Model):
note = models.CharField(max_length=1000)
parent_user = models.OneToOneField(UserProfile, blank=True, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = 'Notes'
def __str__(self):
return self.note
Serializers:
class NoteSerializer(serializers.ModelSerializer):
class Meta:
model = Notes
fields = ('id', 'note', 'parent_user')
urls:
router = routers.DefaultRouter()
router.register('notes', views.UserNoteView)
urlpatterns = [
path('', include(router.urls)),
]
views:
class NoteView(viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = Notes.objects.all()
serializer_class = NoteSerializer
class UserNoteView(NoteView):
def get_queryset(self):
return self.request.parent_user.Notes.all()
My problem is that I can't do for example /William which is the name of my user, and user profile. Someone that knows this must be able to help!
Your notes model is incorrect. Change OneToOneField to ForeignKey. You can change it as below
class Notes(models.Model):
note = models.CharField(max_length=1000)
parent_user = models.ForeignKey(
UserProfile, blank=True, on_delete=models.CASCADE, related_name="notes")
def __str__(self):
return "{note}".format(note=self.note)
Now, change your viewset as below
class UserNoteView(NoteView):
def get_queryset(self):
return self.request.user.userprofile.notes.all()
# or
return Notes.objects.filter(parent_user__user=self.request.user)