Im trying to make a url setup that would display a detail page based on just a year (not month and day), and a pk. like this /YEAR/ISSUE/
This is what I have tried:
My model.py:
class Mag(models.Model):
name = models.CharField(max_length=500)
issue = models.PositiveIntegerField()
pub_date = models.DateTimeField()
unique_together = ("pub_date", "issue")
def __unicode__(self):
return self.name
#models.permalink
def get_absolute_url(self):
creation_date = timezone.localtime(self.pub_date)
return ('mag_detail', (), {
'year': creation_date.strftime('%Y'),
'pk': self.issue})
My views.py:
class MagDetail(DateDetailView):
model = Mag
pk_url_kwarg='pk'
date_field='pub_date'
My urls.py
urlpatterns = patterns('',
url(r'^(?P<year>\d{4})/(?P<pk>\d+)/$', MagDetail.as_view(), name='mag_detail'),
)
But when I try a url like 2014/1, I get an error that month is not specified.
Is it possible to do what I want with DateDetailView, or do I need to look another class?
What if you use a standard DetailView and override the get_object() method, like this:
from django.shortcuts import get_object_or_404
class MagDetail(DetailView):
model = Mag
def get_object(self):
obj = get_object_or_404(
self.model,
pk=self.kwargs['pk'],
pub_date__year=self.kwargs['year'])
return obj
The DateDetailView expects the format of year/month/day
As a general approach, if you will be using this kind of view in your project for other models than Mag alone, you can derive from it though and change the get_object method, roughly like that:
class YearDetailView(DateDetailView):
def get_object(self, queryset=None):
"""
Get the object this request displays.
"""
year = self.get_year()
# Use a custom queryset if provided
qs = queryset or self.get_queryset()
if not self.get_allow_future() and int(year) > datetime.today().year:
raise Http404(_("Future %(verbose_name_plural)s not available because
%(class_name)s.allow_future is False.") % {
'verbose_name_plural': qs.model._meta.verbose_name_plural,
'class_name': self.__class__.__name__,
})
lookup_args = {"%s__year" % self.get_date_field(): year}
qs = qs.filter(**lookup_args)
return super(BaseDetailView, self).get_object(queryset=qs)
class MagDetail(YearDetailView): # note that we derive from our new class!
model = Mag
pk_url_kwarg='pk'
date_field='pub_date'
Related
I'm trying to filter on a serialized value that is not on my model but I can't get my head around this.
Here is my model.py :
class Post(models.Model):
post_id_number=models.AutoField(primary_key=True)
variable_1 = models.CharField(max_length=50)
variable_2 = models.CharField(max_length=50)
variable_3 = models.CharField(max_length=50)
Here is my Views.py :
class PostViewSet(viewsets.ModelViewSet):
serializer_class = PostSerializers
queryset = Post.objects.all()
filterset_class = PostFilter
Here is my serializers.py:
class PostSerializers(serializers.ModelSerializer):
variable_not_in_the_model = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('post_id_number',
'variable_1',
'variable_2',
'variable_3',
'variable_not_in_the_model',
)
def get_variable_not_in_the_model(self, instance):
return instance.variable_1 + ' ' + instance.variable_2 + ' ' + instance.variable_3
Here is my actual filters.py :
from .models import Post
import django_filters
class PostFilter(django_filters.FilterSet):
class Meta:
model = Post
fields = {
'variable_1': ['icontains'],
'variable_2': ['icontains'],
'variable_3': ['icontains']
}
Here is what I would like my filters.py to look like but it doesn't work :
from .models import Post
import django_filters
class PostFilter(django_filters.FilterSet):
class Meta:
model = Post
fields = {
'variable_not_in_the_model': ['icontains']
}
Is there a way I can Filter on the serialized variables and not on the model's variables ?
Thanks so much for your help !
You can't use values from the serializer in the filter. The filter works on the model/db fields. But you can build the custom queryset yourself. If you just want to check if the value is in one of the first three variables, you can try this:
import django_filters
from .models import Post
class PostFilter(django_filters.FilterSet):
search = django_filters.CharFilter(method='filter_search')
class Meta:
model = Post
def filter_search(self, qs, name, value):
if value:
return qs.filter((
Q(variable_1__icontains=value) |
Q(variable_2__icontains=value) |
Q(variable_3__icontains=value)
))
return qs
and then add the ?search=something query param in your API call
Here is how I was able to do :
In my views.py I created a new View (not sure it's ideal since I had to create a new url but since I was using a viewset I wasn't able to to that differently)
class PostAPIView(ListAPIView):
serializer_class = PostSerializers
def get_queryset(self, *args, **kwargs):
#queryset_list = super(PostAPIView, self).get_queryset(*args, **kwargs)
queryset_list = Post.objects.all()
query = self.request.GET.get("q")
if query:
queryset_list = queryset_list.filter(
Q(variable_1__icontains=query) |
Q(variable_2__icontains=query) |
Q(variable_3__icontains=query)
).distinct()
return queryset_list
In my urls.py I've added :
path('api/filter/', PostAPIView.as_view(), name='test'),
And it's working...Not sure it's optimal but working.
Thanks for your help !!!
So I'm attempting to create an automated "tick sheet" as a proof of concept for a proposal to automate a process at my workplace.
At work we receive orders from various Suppliers and need to manually tick off the orders that are received against a list of what is expected on a paper sheet each day.
I'm using the generic DetailView in order to have a separate page for each Retailer and have a model representing the Retailer with a ManyToMany relationship with the Suppliers. I also have an Order model to 'simulate' a connection to the actual WMS database (with the intention of modifying it after getting actual db read access if/when this is put into production.)
I need the expected orders (in the Suppliers ManyToMany relationship) to match against those in the 'real' Order model data, and return an answer as to whether it exists in the db (in a way that I can display it on a template).
After a few frantic attempts to solve it myself I'm a little stumped as to how to achieve this within the context of the DetailView, so I fear I am misunderstanding something...
edit: I should have mentioned I only need the 'supplier code' to match but also intend to have the program check for duplicate orders using the 'order reference' once I've figured this out, as without this functionality the whole thing becomes a bit redundant...
My models.py:
from django.db import models
from django.utils import timezone
class Order(models.Model):
''' To simulate connection to main stock db '''
retailer_code = models.CharField(max_length=4)
retailer_name = models.CharField(max_length=100)
supplier_code = models.CharField(max_length=4)
supplier_name = models.CharField(max_length=100)
order_reference = models.CharField(max_length=20)
despatch_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"< {self.order_reference}', {self.supplier_name}, {self.retailer_name} >"
# -------------------------------------------------------------------------------------
class Retailer(models.Model):
retailer_code = models.CharField(max_length=4)
retailer_name = models.CharField(max_length=100)
suppliers = models.ManyToManyField('Supplier')
slug = models.SlugField(unique=True, null=True)
def get_supplier_values(self):
return [(suppliers.supplier_code + ' - ' + suppliers.supplier_name) for suppliers in self.suppliers.all()]
def save(self, *args, **kwargs):
self.slug = self.slug or slugify(self.retailer_code)
super().save(*args, **kwargs)
def __str__(self):
return f"< {self.retailer_code} - {self.retailer_name} >"
class Supplier(models.Model):
supplier_code = models.CharField(max_length=4)
supplier_name = models.CharField(max_length=100)
def __str__(self):
return f"< {self.supplier_code}, {self.supplier_name} >"
My views.py:
from django.shortcuts import render
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView
from .models import Retailer, Order
class RetailerListView(ListView):
model = Retailer
context_object_name = 'retailer_list'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Select Retailer'
return context
class RetailerDetailView(DetailView):
model = Retailer
slug_field = 'retailer_code'
slug_url_kwarg = 'retailer_code'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Order Checklist'
context['db_orders'] = Order.objects.filter(retailer_code=self.object.retailer_code)
return context
def do_order_checklist(self):
pass # WIP
Any help would be appreciated...
Probably you can use Exists to to annotate if the order is in DB. For example:
from django.db.models import Exists, OuterRef
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Order Checklist'
squery=self.object.suppliers.filter(supplier_code=OuterRef('supplier_code'))
context['db_orders'] = Order.objects.filter(
retailer_code=self.object.retailer_code
).annotate(
in_db=Exists(squery)
)
return context
Then show in template:
{% for item in db_orders %}
{% if item.in_db %}
// do something
{% else %}
// else
{% endif %}
{% endfor %}
I see, probably the answer you are looking for is this.
as you get the list of supplier_codes in each retailer. instance you already have the list.
retailers_supplier_codes = [1, 2, 3, ...]
matching_orders = Order.objects.filter(supplier_code__in = retailers_supplier_codes)
Please consider these three models:
class Movie(models.Model):
name = models.CharField(max_length=254, unique=True)
language = models.CharField(max_length=14)
synopsis = models.TextField()
class TimeTable(models.Model):
date = models.DateField()
class Show(models.Model):
day = models.ForeignKey(TimeTable)
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
And each of them has their serializers:
class MovieSerializer(serializers.HyperlinkedModelSerializer):
movie_id = serializers.IntegerField(read_only=True, source="id")
class Meta:
model = Movie
fields = '__all__'
class TimeTableSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = TimeTable
fields = '__all__'
class ShowSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Show
fields = '__all__'
And their routers
router.register(r'movie-list', views.MovieViewSet)
router.register(r'time-table', views.TimeTableViewSet)
router.register(r'show-list', views.ShowViewSet)
Now I would like to get all the TimeTable objects (i.e. date list) by filtering all the Show objects by a specific movie object. This code seems to be the working and getting the list like I want it
m = Movie.objects.get(id=request_id)
TimeTable.objects.filter(show__movie=m).distinct()
But I have no clue how to use this in django rest framework? I tried doing this way (which I am pretty sure its wrong), and I am getting error:
views.py:
class DateListViewSet(viewsets.ModelViewSet, movie_id):
movie = Movie.objects.get(id=movie_id)
queryset = TimeTable.objects.filter(show__movie=movie).distinct()
serializer_class = TimeTableSerializer
urls.py:
router.register(r'date-list/(?P<movie_id>.+)/', views.DateListViewSet)
error:
class DateListViewSet(viewsets.ModelViewSet, movie_id):
NameError: name 'movie_id' is not defined
How can I filter using viewsets in django rest framework? Or if there is any other prefered way than please list it out. Thank you.
ModelViewSet by design assumes that you want to implement a CRUD(create, update, delete)
There is also a ReadOnlyModelViewSet which implements only the GET method to read only endpoints.
For Movie and Show models, a ModelViewSet or ReadOnlyModelViewSet is a good choice whether you want implement CRUD or not.
But a separate ViewSet for a related query of a TimeTable which describes a Movie model's schedule doesn't looks so good.
A better approach would be to put that endpoint to a MovieViewSet directly. DRF provided it by #detail_route and #list_route decorators.
from rest_framework.response import Response
from rest_framework.decorators import detail_route
class MovieViewSet(viewsets.ModelViewset):
queryset = Movie.objects.all()
serializer_class = MovieSerializer
#detail_route()
def date_list(self, request, pk=None):
movie = self.get_object() # retrieve an object by pk provided
schedule = TimeTable.objects.filter(show__movie=movie).distinct()
schedule_json = TimeTableSerializer(schedule, many=True)
return Response(schedule_json.data)
This endpoint will be available by a movie-list/:id/date_list url
Docs about extra routes
Register your route as
router.register(r'date-list', views.DateListViewSet)
now change your viewset as shown below,
class DateListViewSet(viewsets.ModelViewSet):
queryset = TimeTable.objects.all()
serializer_class = TimeTableSerializer
lookup_field = 'movie_id'
def retrieve(self, request, *args, **kwargs):
movie_id = kwargs.get('movie_id', None)
movie = Movie.objects.get(id=movie_id)
self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
Use a retrieve method, which will match any GET requests to endpoint /date-list/<id>/.
Advantage is that you don't have to explicitly handle the serialization and returning response you make ViewSet to do that hard part. We are only updating the queryset to be serialized and rest framework does the rest.
Since ModelViewSet is implemented as,
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
Its implementation includes the following methods (HTTP verb and endpoint on bracket)
list() (GET /date-list/)
create()(POST /date-list/)
retrieve()(GET date-list/<id>/)
update() (PUT /date-list/<id>/)
partial_update() (PATCH, /date-list/<id>/
destroy() (DELETE /date-list/<id>/)
If you want only to implement the retrieve() (GET requests to endpoint date-list/<id>/), you can do this instead of a `ModelViewSet),
from rest_framework import mixins, views
class DateListViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = TimeTable.objects.all()
serializer_class = TimeTableSerializer
lookup_field = 'movie_id'
def retrieve(self, request, *args, **kwargs):
movie_id = kwargs.get('movie_id', None)
movie = Movie.objects.get(id=movie_id)
self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
The error
class DateListViewSet(viewsets.ModelViewSet, movie_id): NameError: name 'movie_id' is not defined
happens because movie_id is being passed as parent class of DataListViewSet and not as parameter as you imagined
This example in the documentation should be what you are looking for.
Adjust your URL:
url(r'date-list/(?P<movie_id>.+)/', views.DateListView.as_view())
Adjust your Model:
class Show(models.Model):
day = models.ForeignKey(TimeTable, related_name='show')
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
Your view would look like this:
class DateListView(generics.ListAPIView):
serializer_class = TimeTableSerializer
def get_queryset(self):
movie = Movie.objects.get(id=self.kwargs['movie_id'])
return TimeTable.objects.filter(show__movie=movie).distinct()
Another way to do it would be:
Adjust your URL:
router.register(r'date-list', views.DateListViewSet)
Adjust your Model:
class Show(models.Model):
day = models.ForeignKey(TimeTable, related_name='show')
time = models.TimeField(choices=CHOICE_TIME)
movie = models.ForeignKey(Movie)
class Meta:
unique_together = ('day', 'time')
Your view would look like this:
class DateListViewSet(viewsets.ModelViewSet):
serializer_class = TimeTableSerializer
queryset = TimeTable.objects.all()
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('show__movie_id')
Which will allow you to make requests such as:
http://example.com/api/date-list?show__movie_id=1
See documentation
Ivan Semochkin has the correct answer but the detail decorator is deprecated. It was replaced by the action decorator.
from rest_framework.decorators import action
class MovieViewSet(viewsets.ModelViewset):
#action(detail=True)
def date_list(self, request, pk=None):
movie = self.get_object() # retrieve an object by pk provided
schedule = TimeTable.objects.filter(show__movie=movie).distinct()
schedule_json = TimeTableSerializer(schedule, many=True)
return Response(schedule_json.data)
To improve #all-is-vanity answer, you can explicitly use movie_id as a parameter in the retrieve function since you are overriding the lookup_field class property:
def retrieve(self, request, movie_id=None):
movie = Movie.objects.get(id=movie_id)
self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
You can also call self.get_object() to get the object:
def retrieve(self, request, movie_id=None):
movie = self.get_object()
self.queryset = TimeTable.objects.filter(show__movie=movie).distinct()
return super(DateListViewSet, self).retrieve(request, *args, **kwargs)
can anyone tell me how to set a slug in a view?
I pretend to use name as slug,
def editar_cliente(request, pk):
detail = database.objects.get(pk=pk)
name = detail.name
company = detail.company
pk = detalle.pk
return render(request, 'edit_client.html'company': company, 'pk':pk})
I would create slug in the model's save method:
from django.utils.text import slugify
class Client(models.Model):
name = models.CharField(max_length=30)
slug = models.SlugField(editable=False) # hide from admin
def save(self):
if not self.pk:
self.s = slugify(self.name)
super(Client, self).save()
But you could use the same approach to set it in view as well:
from django.utils.text import slugify
def editar_cliente(request, pk):
detail = database.objects.get(pk=pk)
name = detail.name
company = detail.company
pk = detalle.pk
slug = slugify(name)
return render(request, 'edit_client.html'company': company, 'pk':pk})
Hope it helps.
I had a Product object in models,
from django.db import models
class Product(models.Model):
title = models.CharField(max_length=255, unique = True)
description = models.TextField()
image_url = models.URLField(verify_exists=True, max_length=200, blank = True, null = True)
quantity = models.PositiveSmallIntegerField(default=0)
def sell(self, save=True):
self.quantity -= 1
if save:
self.save()
and a template called:product_view.html Product {{product.title}}
and a template called:product_list.html {% for p in products %}{{p.id}},{% endfor %}
I want to make the view retrieve the Product object from the database, use it to render the template and finally return an HttpResponse object that contains the resulting string. If the Product with the given product_id can not be found raise a 404 exception (or return HttpResponseNotFound)
def productview(request, product_id):
"""
I dont know how to write here
"""
#render template "product_view.html" with param product
return HttpResponse("product %s" % product_id)
Meanwhile,if i want to render a page with a list of all available products. A product is available if it has a quantity that's bigger than 0. The template product_list.html is expecting a single parameter products in the context which refers to an iterable of Product objects.
So how to make the view retrieve the the available products and how to use them to render the template and return an HttpResponse object that contains the resulting string?
def available_products(request):
"""
I dont know how to write here
"""
#render template "product_list.html" with param products
return HttpResponse("View not implemented!")
Thx very much
have you tried with generic views?
class ProductDetailView(DetailView):
model = Product
template_name = 'product_view.html'
class ProductListView(ListView):
model = Product
template_name = 'product_list.html'
def get_queryset(self):
"""
Here you can filter the product list
"""
query_set = super(ProductListView, self).get_queryset()
return query_set.filter(quantity__gt=0)
#views.py
from django.shortcuts import render_to_response
from .models import Product
def productview(request):
"""
Retrive all products
"""
products = Product.objects.all()
return render_to_response('product_list.html',{'products': products})
def available_products(request):
"""
Retrive available products
"""
products = Product.objects.filter(quantity__gt=0)
return render_to_response('product_list.html',{'products': products})
These two also can be combined in one view, depending on defined url pattern.
For example:
#urls.py
urlpatterns = patterns('',
url(r'^product/(?P<which>\w+)/$', 'app_label.views.product_view'),
)
#views.py
def product_view(request, which):
"""
Retrive available products
"""
if which == 'all':
products = Product.objects.all()
else:
products = Product.objects.filter(quantity__gt=0)
return render_to_response('product_list.html',{'products': products})