0

I have a django page that displays a list of links. Each link points to the detail page of the respective object. The link contains the pk/id of that object (something like ../5/detailObject/). The list is generated on the backend and has some filtering baked into it, e.g. only generate a link if that object has state x, etc.

Clicking on the links works, but users can still manipulate the url and pass a valid link with an incorrect state (a wrong pk/id is being handled with the get or 404 shortcut).

What is the best practice for handling this kind of scenario with django? Should that kind of filtering be placed in the object's model class instead of using function-based views as I do now?

Andy
  • 3,132
  • 4
  • 36
  • 68
Vvandil
  • 23
  • 2
  • Intead of pk you can use a slug. If your model has a slug field – Dimitris Kougioumtzis Sep 12 '18 at 17:48
  • Not at the moment, i have seen slug here and there but never took notice of it. Is there a good starting point to dig into Slugs. Any hurdles implementing a slug after the fact that a django newbie should be aware of?. – Vvandil Sep 12 '18 at 18:05
  • See that answer to get an idea about slug in django https://stackoverflow.com/a/427160/924300 – Dimitris Kougioumtzis Sep 12 '18 at 18:14
  • @DimitrisKougioumtzis talking about slugs appears to be completely irrelevant to the OP's question, which is about where to put shared logic. Vvandil, yes putting this logic as methods of the model is a good idea, or possibly as methods on a custom Manager class. – Daniel Roseman Sep 12 '18 at 19:35
  • The way i understand it, even with a slug, the user could still manipulate the url and access the "detail" page with an object that was prefiltered. – Vvandil Sep 12 '18 at 20:21

1 Answers1

0

Function based view:

If you want to restrict a set of objects to a particular user (for instance a user's orders), then you would need to set up the Order model to foreign key to the User model and then look up the order by both id and user:

views.py:

def get_order(request, id=0)
    if request.method == 'GET':
        try:
            order = Order.objects.get(user=request.user, pk=id)
        except Order.DoesNotExist:
            return redirect(...)

And set up a url to handle:

url(r'^order/(?P<id>\d+)/$', views.get_order, name='get_order_by_id'),

As far as adding a slug field on the model after the fact, set up a second url:

url(r'^order/(?P<slug>[\w-]+)/$', views.get_order, name='get_order_by_slug')

And change the above view logic to first do a lookup by pk if pk is greater than 0 and then redirect back to the function using the slug from the looked up order (this assumes all looked-up records have slugs):

def get_order(request, slug='', id=0)
    if request.method == 'GET':
        try:
            if id > 0:
                order = Order.objects.get(user=request.user, pk=id)
                return redirect(reverse('get_order_by_slug'), permanent=True, slug=order.slug)
            order = Order.objects.get(user=request.user, slug=slug)
        except Order.DoesNotExist:
            return redirect(...)

You should also put unique=True on the slug field and ensure that the user is authenticated by placing the @login_required decorator on your view.

To restrict orders by a particular status, you could:

Create a set of statuses for your Order model, and then you could:

  • Pass a value for a kwarg in the view when you filter, or
  • Create a custom manager on the Order model

There are several ways you could create your statuses:

  • as a set of choices on the Order model
  • use the SmartChoices library
  • as a database field

If you create choices on the Order model, it could be something like this:

class Order(models.model):
    STATUSES = (
        ('PLCD', 'Placed'),
        ('INTR', 'In Transit'),
        ('DLVR', 'Delivered')
    )

    status = models.CharField(max_length=4, default='', choices=STATUSES)

An acquaintance who is a very seasoned Django professional told me about the SmartChoices library. I have not used it yet but would like to try it at some point. The database field option would be my least preferred way of doing this because that seems to me like moving programming variables into the database; however, it would work.

Dan Swain
  • 2,910
  • 1
  • 16
  • 36
  • Thanks for your detailed answer. In your example my Order object would not only belong to a user, but would also have one of many states (say placed, in process, in transit etc..). Now outside of the "detail view" i would generate the list of all orders being processed and only those would be listed and linked towards the detail view. On top of that within the detail view i would need to check if that given order, via url, is in the correct state to be viewed. My question would be where to put that logic (only orders being processed are allowed to be viewed in detail etc..) – Vvandil Sep 14 '18 at 21:26
  • @Vvandil I fixed a couple of typos on the Order model. – Dan Swain Sep 16 '18 at 01:03