How to display a query on DetailView in Django? - django

I want my "CustomDetailView" to display a query(a single "flashcard"). I was able to it by using ListView
CustomListView(ListView):
model = Flashcard
template_name = 'flashcards.html
queryset = Flashcard.objects.all()[:1]
But for DetaiView I'm getting this error
Generic detail view CustomDetailView must be called with either an object pk or a slug in the URLconf.
class CustomDetailView(DetailView):
model = Flashcard
template_name = "flashcards.html"
queryset = Flashcard.objects.all().first()
urls.py
path('', CustomDetailView.as_view(), name='flashcards'),
How to fix this?

remove first from queryset:
class CustomDetailView(DetailView):
model = Flashcard
template_name = "flashcards.html"
queryset = Flashcard.objects.all()
lookup_field = 'pk'
lookup_url_kwarg = 'pk'
enter code here
and add id to url:
path('/<int:pk>/', CustomDetailView.as_view(), name='flashcards'),

You need your pk or slug in
path('', CustomDetailView.as_view(), name='flashcards'),
See an example from the docs:
urlpatterns = [
path('<slug:slug>/', ArticleDetailView.as_view(), name='article-detail'),
]
This is because DetailView is inheriting from SingleObjectMixin, and it fetches objects that way. If you want, jump to definition of this class in your IDE to see its implementation of get_queryset and get_object.
How you call your pk or slug really depends on your model, the Flashcard. If your primary_key field is an Integer, I think you'll be fine writing
path('<int:pk>', CustomDetailView.as_view(), name='flashcards'),
edit: as Pourya Mansouri wrote, you do need to remove queryset attribute in your case too
The slug you can customize with slug_field and similar attributes.
You can define a completely custom object fetching behavior by overriding get_object or get_queryset. Here's a random examples of doing that I found on the internet:
Overriding get_object: https://www.valentinog.com/blog/detail/
Overriding get_queryset: https://www.agiliq.com/blog/2019/01/django-when-and-how-use-detailview/

Maybe should have better communicated. Sorry for my English.
I wanted to return a random query when I clicked on a link a show it on detailView. I was able to it by this. Don't think it's efficient. If anyone has any idea share it.
def get_object(self):
queryset = Flashcard.objects.order_by('?').first()
return queryset

Related

Getting Object based on its ID

Setup
I am trying to write a DeleteView that will delete an object based on its Id. The object is a journal and I want to reference the Journal that the user is currently located in. So for example if User1 is in Journal "Work" I want to delete that specific one based on journal Id and not anything else.
My understanding is that Django creates an ID fields (Autofield) for each model.
Error
This is my current view:
class DeleteJournal(LoginRequiredMixin, DeleteView):
model = Journal
tempalte_name = 'delete_journal.html'
success_url = reverse_lazy('home')
def get_object(self, queryset=None):
id = self.kwargs['id']
return self.get_queryset().filter(id=id).get()
The error I receive is this:
What is the solution to this and why is it not working?
EDIT 1
urlpatterns = [
path('', CreateToJournal.as_view(), name='to-journals'),
path('<slug:slug>', ToJournalEntriesList.as_view(), name='to-journal-entries'),
path('<slug:slug>/delete', DeleteJournal.as_view(), name='delete-journal'),
]
Please note, I do not want to delete based on slug, because a journal can have the same name and hence the same slug for different user. The only unique value is id, which is why I need to get id for the "current" journal.
Thanks a ton in advance, really appreciate anyone looking at this.
In order to delete the object without the pk or id on slug you have to use the get_object method on your DeleteView
def get_object(self):
return Journal.objects.get(pk=self.request.GET.get('pk'))
There's a similar question here:
Django UpdateView without pk in url
Alright, unfortunately the answer from Luis Silva did not help. I found out that the get_object() function requires a pk_url_kwarg set and after a little digging I found this StackOverflow post: Arguments of DetailView methods and usage of pk_url_kwarg.
This is the code for DeleteView:
views.py
class DeleteJournal(LoginRequiredMixin, DeleteView):
model = to_journal
tempalte_name = 'to_journals/delete_journal.html'
success_url = reverse_lazy('home')
pk_url_kwarg = 'id'

How to return None if no request is made using django-filter

Hello I am a bit puzzled on how to achieve this. I have a filter built with django filter() method and by default it returns a list of objects in the database.I don't want to display the list until a filter is made/searched how do I go about that?
You can simple check url Get parameter before filter.
Here is an example using function based views.
Here Article model has title field and value of title is being checked in get parameters. If none just return empty list
class ArticleListView(FilterView):
model = Article
template_name = 'post/article_list.html'
filterset_class = ArticleFilter
def article_search(request):
user_list = Article.objects.all()
if request.GET.get('title'):
# Add more logics based on your needs
user_filter = ArticleFilter(request.GET, queryset=user_list)
return render(request, 'post/article-list-filter.html', {'filter':user_filter})
return render(request, 'post/article-list-filter.html', {'filter':[]})
There is an optional Meta argument 'strict' on FilterSet that defines the behavior if filterset is not valid, as mentioned in the docs here
from django_filters import FilterSet, STRICTNESS
class ProductFilter(FilterSet):
class Meta:
model = Product
fields = ['name', 'release_date']
strict = STRICTNESS.RETURN_NO_RESULTS
FilterView also defines a logic for filtering, where no results should be returned if strict=True property is set on the FilterView or filterset is not valid or bound, it is located here

use django-filter with object url_params

I have successfully implemented django-filter on my Django-rest-framework server.
I have the following filter_class
filters.py
class EmploymentFilter(filters.FilterSet):
class Meta:
model = EmploymentCheck
fields = ['instructions',]
views.py
class EmploymentCheckViewSet(viewsets.ModelViewSet):
pagination_class = ContentRangeHeaderPagination
serializer_class = EmploymentCheckSerializer
queryset = EmploymentCheck.objects.all()
filter_class = EmploymentFilter
the filter works when i send a get request as
/employmentcheck/?instructions=2
However, I have implemented a front end with react-admin.
My front-end, sends a request with the url_params as objects
/employmentcheck/?filter={"instruction_id":"2"}&range=[0,24]&sort=["id","DESC"]/
Notice how the URL specifies a filter object, in which, it defines the parameters to filter against.
My question is, how and where can I filter my model without changing the URL pattern from my client?
Any other advise that spans the scope of my question is equally welcomed
Models.py
class EmploymentCheck(models.Model):
instructions = models.ForeignKey(Instruction, on_delete=models.CASCADE, null=True)
I simply omitted the filter_class, and instead overrode the get_queryset() method of the viewset.
Allow me to make some changes, the commented out code would fail if you sent a request with no 'filter' query params. e.g GET:/api/employee/ as opposed to GET:/api/employee/?filter{"instruction"=""}
So I opted to check if the query_params.get('key') was none, if so, i pass an empty string.
Any better pythonic way of handling this is encouraged
ViewSet
class EmploymentCheckViewSet(viewsets.ModelViewSet):
serializer_class = EmploymentCheckSerializer
def get_queryset(self):
queryset = EmploymentCheck.objects.all()
if self.request.query_params.get('filter') is None:
return queryset #return the queryset as is
else:
_instructions = self.request.query_params.get('filter') #returned as a string
_instructions = json.loads(_instructions)#convert to dictionary
if queryset and any(_instructions):
queryset = queryset.filter(instructions = _instructions['instruction_id'])
return queryset
Note: When you override the get_queryset, you must explicitly define the base_name argument of the router.register method in your app.urls.py. Refer to django-rest-framework routers.
urls.py
router = DefaultRouter()
router.register(r'employmentcheck', views.EmploymentCheckViewSet, 'EmploymentCheck')#explicitly set the base_name attribute when the viewset defines a custom get_queryset method

django rest framework change primary key to use a unqiue field

I have a model which is called GameProfile, which is a one to one relation with User model. I used HyperlinkedModelSerializer across all my design.
For the GameProfile, the user field is suppose to be the primary key for querying, it is unique but I did not set it up as a primary key. Is there a way to change the default behavior of django serializer to point to user__id as the primary key and always use it for retreiving the profile in the detail view?
class GameProfileSerializer(serializers.HyperlinkedModelSerializer):
"""
"""
user_pk = serializers.Field(source='user.id')
class Meta:
model = GameProfile
class GameProfileViewSet(viewsets.ModelViewSet):
"""
"""
queryset = GameProfile.objects.all()
serializer_class = GameProfileSerializer
def get_queryset(self):
""" get_queryset
"""
queryset = super(GameProfileViewSet, self).get_queryset()
if not queryset.exists():
raise Http404
if self.request.user.is_authenticated() and not self.request.user.is_superuser:
return queryset.filter(user=self.request.user)
return queryset
please advise, thanks in advance:)
Assuming your GameProfile model looks like:
class GameProfile(models.Model)
user = models.OneToOneField('User')
The serializer will be:
class GameProfileSerializer(serializers.HyperlinkedModelSerializer):
user_id = serializers.Field(source='user.id')
class Meta:
model = GameProfile
Set the .lookup_field attribute on the view correctly:
lookup_field = 'user_id'
Url will be:
/gameprofile/<user_id>
In order to get the URLs to work, you might need to add lookup_field on the ViewSet, not just on the serializer. So you would have:
class GameProfileViewSet(viewsets.ModelViewSet):
queryset = GameProfile.objects.all()
serializer_class = GameProfileSerializer
lookup_field = 'user__id'
In this case the lookup_field uses the double-underscore notation rather than the dot notation (dots won't work in the regular expressions in the URL patterns). I was unable to get the solution proposed by #almalki and #patsweet to work; according to the documentation on serializers, "The value of this option [lookup_field] should correspond both with a kwarg in the URL conf, and with a field on the model", so it's possible that it doesn't work with RelatedFields.
If I'm understanding your question correctly, you want a url structure like so:
/api/<GameProfile-resource>/<user-pk>
If that is the case, you should checkout the lookup_field option. Link
You're Serializer class would look something like:
class GameProfileSerializer(serializers.HyperlinkedModelSerializer):
"""
"""
user_pk = serializers.Field(source='user.id')
class Meta:
model = GameProfile
lookup_field = 'user_pk' # Might have to use 'user__id'

Django: hiding the primary key (pk) and using DetailView with get_queryset

Following up on this thread: django: How do I hash a URL from the database object's primary key?, I would like to hide primary keys from users and use Detailview in my urlconf. I was able to accomplish this hiding using bitwise XOR for the most part (it worked in my view functions), until I got to the part where I had to "unmask" the masked primary key that was sent in the url to my subclass of DetailView.
How do I "unmask" my pk_masked named group before sending it to my DetailViewFilteredOnUser(DetailView) instance? Is there a way to send mask_toggle(pk_unmasked) to my call to DetailViewFilteredOnUser right in the urlconf? In searching for solutions, I found something about pk_url_kward in the Django documentation, but I couldn't get it to work and anyways I don't think that can help me perform operations on the primary key that DetailView operates on.
Here is my masking function:
def mask_toggle(number_to_mask_or_unmask):
return int(number_to_mask_or_unmask) ^ settings.MASKING_KEY
My models are "pkgs" that contain "items":
class Pkg(models.Model):
user = models.ForeignKey(User, editable=False)
tracking_number = models.CharField(max_length=60, unique=True)
class Item(models.Model):
pkg = models.ForeignKey(Pkg)
description = models.CharField(max_length=300)
Here is what is in my urls.py:
class ListViewFilteredOnUser(ListView):
def get_queryset(self):
return Pkg.objects.order_by('-created_at').filter(user=self.request.user)
class DetailViewFilteredOnUser(DetailView):
def get_queryset(self):
qs = super(DetailViewFilteredOnUser, self).get_queryset()
return qs.filter(user=self.request.user)
....
url(r'^(?P<pk_masked>\d+)/$',
login_required(DetailViewFilteredOnUser.as_view( model=Pkg,
template_name='pkgs/detail.html'
)),
name='detail'),
So the problem is that if my named group in my urlconf is "pk", then a masked primary key (because the masked key is what is in the url) is sent to DetailView. If my named group in my urlconf is "pk_masked", then I need to do pk=mask_toggle(pk_masked) somewhere, and I can't figure out where or how to do this. Thanks.
If I understood your problem correctly, you need to override get_object (which makes get_queryset pretty much irrelevant, but you can still use it for clarity). Something like:
class DetailViewFilteredOnUser(DetailView):
model = Pkg
template_name = 'pkgs/detail.html'
def get_queryset(self):
return super(DetailViewFilteredOnUser, self).get_queryset().filter(user=self.request.user)
def get_object(self):
return self.get_queryset().get(pk=mask_toggle(self.kwargs.get("pk_masked"))
(Of course, don't forget to catch exceptions, I left that out for clarity and brevity.)