setting get_absolute_url when using a router - django

I'm working on a Comment app and I would like my Commentserializer to display the exact URL of each instance of Comment. I know that I have to use the get_absolute_url of the Comment model. But i cannot connect my viewnames from my router to the get_absolute_url.
Here is my Model :
class Comment(models.Model):
content = models.TextField(max_length=150)
author = models.ForeignKey(
User,
on_delete = models.CASCADE
)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField(blank=True)
content_object = GenericForeignKey('content_type', 'object_id')
parent = models.ForeignKey(
"self",
on_delete = models.CASCADE,
blank=True,
null=True
)
datestamp = models.DateTimeField(auto_now_add=True)
objects = CommentManager()
def __str__(self):
return str(self.content[:30])
def save(self):
self.object_id = self.parent.id
super(Comment, self).save()
def children(self):
return Comment.objects.filter(parent=self)
def get_absolute_url(self):
return reverse("comments-details", args=[str(self.id)])
#property
def is_parent(self):
if self.parent is None:
return False
return True
and here is my router :
router = router = routers.SimpleRouter()
router.register('api/comments', CommentViewSet)
urlpatterns = router.urls
As you can see, I'm trying to use "comment-details" as a viewname.
The end Goal is to display a JSON like that :
{ url : 'blabla/comments/{pk}/details }

Okay It was easy to fix. Just use a HyperlinkedModelSerializer and add 'url' to your fields like so :
class CommentSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Comment
fields = [
"url",
"datestamp",
"content",
"is_parent",
"object_id",
"children"
]

Related

How to use detailview pk in createview

# 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

Multiple generic class based DetailView's for the SAME model

I am attempting to use two detail views that will render different templates for the same model, at different URLs of course. Is it possible to have different generic detail views for the same model? If not I'll just have to write my own I suppose. All my detail views route to the absoluteurl but in this case I want each detail view to route to the template I have defined in the class.
I used the method below to successfully create multiple list views and update views, but it just doesn't work on detail views, I always end up at "course_detail" even though I declared "course_detail_producer_view."
models.py
class Course(models.Model):
class Meta:
verbose_name = "Course"
verbose_name_plural = "Courses"
ordering = ['start_time']
pub_partner_choices = [
("company1", "company1"),
('company2', 'company2'),
]
status_choices = [
('hold', 'Hold'),
('editorial', 'Editorial'),
('approved', 'Approved'),
('cancelled', 'Cancelled'),
]
title = models.CharField(max_length=200)
slug = AutoSlugField(max_length=100, help_text="course title",
populate_from=['title', 'date'], unique=True, )
start_time = models.TimeField(blank=True, null=True)
end_time = models.TimeField(blank=True, null=True)
date = models.DateField(blank=True, null=True)
new_course = models.BooleanField(default=False)
new_instructor = models.BooleanField(default=False)
katacoda = models.BooleanField(default=False)
jupyterhub = models.BooleanField(default=False)
released = models.BooleanField(default=False)
status = models.CharField(max_length=50,
choices=status_choices,
blank=False
)
pub_partner = models.CharField(max_length=50,
choices=pub_partner_choices,
blank=False)
course_notes = models.TextField(max_length=500,
blank=True,
)
producer_notes = models.TextField(max_length=500, blank=True)
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
editable=False
)
producer = models.ManyToManyField(Producer,
related_name="course",
blank=True,
)
def get_absolute_url(self):
"""Return URL to detail page of a Course"""
return reverse(
"course_detail",
kwargs={"slug": self.slug}
)
def __str__(self):
date_time = f"{self.date} {self.start_time}"
return f"{self.title} : {date_time}"
urls.py
path(
'courses/<str:slug>/',
CourseDetail.as_view(),
name='course_detail'
),
path(
'courses/producer-view/<str:slug>/',
CourseDetailProducerView.as_view(),
name='course_detail_producer_view'
),
views.py
class CourseDetail(LoginRequiredMixin, DetailView):
queryset = Course.objects.all()
template_name = 'lots_app/course_detail.html'
class CourseDetailProducerView(LoginRequiredMixin, DetailView):
queryset = Course.objects.all()
template_name = 'lots_app/course_detail_producer_view.html'
My problem was being caused by an UpdateView that was using reverse_lazy to get the absolute_url. It turns out there was no problems with the DetailViews, just how I was trying to get to them (whoops!) The solution was to override get_success_url(). The now corrected UpdateView looks like this:
class CourseUpdateProducerView(LoginRequiredMixin, UpdateView):
form_class = CourseFormProducerView
model = Course
template_name = 'lots_app/course_form_producer_view.html'
extra_context = {"update": True}
def get_success_url(self, **kwargs):
"""Return the URL to redirect to after processing a valid form."""
url = reverse_lazy('course_detail_producer_view',
kwargs={"slug": self.object.slug})
return url

Two slugs in url

I want to have a url pattern that takes 2 slugs. I'm trying to make it look like http://127.0.0.1:8000/category/model but I'm having difficulties understanding how to do this.
Below is what I have so far:
models.py
def model_detail_view(request, category_slug, model_slug):
model = Model.objects.get(
category__slug=category_slug, model_slug=model_slug)
context = {
"model": model,
}
return render(request=request, template_name='main/model_detail.html', context=context)
urls.py
path("<str:category_slug>/<str:model_slug>/", views.model_detail_view, name="model_detail_view"),
models.py
class Category(models.Model):
title = models.CharField(max_length=50)
featured_image = models.ImageField(upload_to="categories")
category_slug = AutoSlugField(null=True, default=None,
unique=True, populate_from='title')
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.title
class Model(models.Model):
title = models.CharField(max_length=80)
category = models.ManyToManyField(Category)
featured_image = models.ImageField(upload_to=image_dir)
model_slug = AutoSlugField(null=True, default=None,
unique=True, populate_from='title')
class Meta:
verbose_name_plural = "Models"
def __str__(self):
return self.title
Try this,
model = model = Model.objects.get(category__category_slug=category_slug, model_slug=model_slug)
Reference:
Django:Lookups that span relationships---(Django Doc)

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)

Django rest framework, change ForeignKey

i'm fighting with DRF too long so now i must ask question.. How change ForeignKey to another? I have user profile and relation to status model.
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
ldap_uid = models.CharField(max_length=100, blank=True, null=True, default=None)
redmine_id = models.IntegerField(blank=True, null=True, default=None)
status = models.ForeignKey(Status, models.SET_NULL, blank=False, null=True, default=DEFAULT_STATUS_ID)
location = models.ForeignKey(Location, models.SET_NULL, blank=False, null=True, default=DEFAULT_LOCATION_ID)
online = models.BooleanField(default=False)
class SelectValuesModel(models.Model):
name = models.CharField(max_length=100)
display_name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
abstract = True
class Status(SelectValuesModel):
pass
class Location(SelectValuesModel):
pass
What is good way to change Profile status to another? I'm trying with something like this without success
views.py
class UserStatusView(viewsets.ViewSet):
def partial_update(self, request, pk=None):
user = User.objects.get(pk=pk)
user_profile = user.profile
new_stauts = Status.objects.get(request.data.status)
serialized_data = ProfileSerializer(user_profile)
if(serialized_data.is_valid()):
serialized_data.save(status=new_stauts)
return Response(serialized_data.errors)
And trying send new id via PATCH. I'm trying tto find solution but no success here too. And how do it good? Make another route for updating Profile status? Or make something like profile/1/update_status/2? Now my routing looks like:
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'redmine', views.RedmineCurrentTaskView, base_name='redmine')
router.register(r'parameters', views.ParametersView, base_name='parameters')
router.register(r'update_status', views.UserStatusView, base_name='update_status')
router.register(r'debug', views.DebugStatus, base_name='debug')
urlpatterns = [
path('', views.index, name='index'),
path('api/', include(router.urls))
]
And serializers.py
class SelectValuesSerializer(serializers.ModelSerializer):
class Meta:
fields = ('pk', 'name', 'display_name')
class LocationSerializer(SelectValuesSerializer):
class Meta(SelectValuesSerializer.Meta):
model = Location
class StatusSerializer(SelectValuesSerializer):
class Meta(SelectValuesSerializer.Meta):
model = Status
class ProfileSerializer(serializers.ModelSerializer):
status = StatusSerializer()
location = LocationSerializer()
class Meta:
model = Profile
fields = ('status', 'location', 'online', 'redmine_id')
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer(read_only=True)
class Meta:
model = User
fields = ('pk', 'first_name', 'profile')
read_only_fields = ('first_name',)
Just pass request.data to the serializer with partial=True argument:
class UserStatusView(viewsets.ViewSet):
def partial_update(self, request, pk=None):
user = User.objects.get(pk=pk)
user_profile = user.profile
serialized_data = ProfileSerializer(user_profile, data=request.data, partial=True)
if serialized_data.is_valid():
serialized_data.save()
return Response(serialized_data.data)
return Response(serialized_data.errors)
You need to provide status_id with request body like this:
{"status": 1}
UPD
To pass status as id change your serializer to this:
class ProfileSerializer(serializers.ModelSerializer):
location = LocationSerializer()
class Meta:
model = Profile
fields = ('status', 'location', 'online', 'redmine_id')
def to_representation(self, instance):
self.fields['status'] = StatusSerializer()
return super(ProfileSerializer, self).to_representation(instance)
This allows to post status_id, but get status details with your API.