1

I'm trying to implement a mechanism were by only 1 one query will be made in the database after the query, a boolean field in the database will change its status from true to false. I don't know how to accomplish this.

The Model

class Code(models.Model):
    best_before = models.DateTimeField(auto_now=True)
    expiry_date = models.DateTimeField(auto_now=True)
    code = models.CharField(max_length=8, blank=True, unique=True)
    status = models.BooleanField(blank=True, default=False)

And I'm using a get request to query code field in the database.

View

def index(request):
    info = Code.objects.all()
    query = request.GET.get('q')
    print(query)
    if query:
        info = info.filter(code__exact=query)
    context = {
      'info':info,
      'query':query
     }
    return render(request, 'drugs/index.html', context)
Martins
  • 1,130
  • 10
  • 26

2 Answers2

3

You can do it like this:

query = request.GET.get('q', None)
if query:
    info.filter(code__exact=query).update(status=False)

Update

As @brunoDesthuilliers has mentioned, technically a GET request must be idempotent, meaning everytime you make a GET request, each time, the response should be same. In short, you should not make an update in GET request. And its frankly, its not safe to Update using GET request. Now, alternativly, you can use POST requests. You can checkout django's documentation of using form for POST request. I will provide a short example:

# template (drugs/form.html)
<form action="/your-url/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="OK">
</form>

# form

from django import forms

class QueryForm(forms.Form):
    query = forms.CharField(label='Query', max_length=100)

# view

def index(request):
    form = QueryForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        query = form.cleaned_data.get('query')
        info = Code.objects.all()  
        info = info.filter(code__exact=query)
        info.update(status=False)
        context = {
          'info':info,
          'query':query
         }

         return render(request, 'drugs/index.html', context)
    else:
        return render(request, 'drugs/form.html', {'form': form})
ruddra
  • 50,746
  • 7
  • 78
  • 101
  • a GET request must be idempotent. – bruno desthuilliers Jun 18 '19 at 12:47
  • @brunodesthuilliers sorry, my bad. Thanks for reminding me of that. I have mentioned it in the update section. Thanks – ruddra Jun 18 '19 at 13:00
  • "meaning everytime you make a GET request, each time, the response should be same" -> the response doesn't have to be "the same" actually - check this for a more in-depth definition: https://stackoverflow.com/questions/45016234/what-is-idempotency-in-http-methods – bruno desthuilliers Jun 18 '19 at 13:24
  • also a successful POST should be followed by a redirect (cf https://en.wikipedia.org/wiki/Post/Redirect/Get) to prevent the same POST request from being submitted more than once. – bruno desthuilliers Jun 18 '19 at 13:29
-1

Simple answer is to just do it in your view.

    if query:
        info = info.filter(code__exact=query)
        if info[0].status:
            info[0].status = False
            info[0].save()

Was there something more sophisticated you were looking for? Maybe doing it under the covers in the model?

Deepstop
  • 3,627
  • 2
  • 8
  • 21
  • A GET request _must_ be idempotent. – bruno desthuilliers Jun 18 '19 at 12:29
  • Oh and yes: `.filter()` returns a queryset, not a model instance. – bruno desthuilliers Jun 18 '19 at 12:30
  • @brunodesthuilliers So i'll use a 'get' instead? – Martins Jun 18 '19 at 12:45
  • @Martins yes you should use `.get()` to retrieve a single model instance - but beware, if you rebind `info` you'll end up with inconstant data in your template context, depending on whether ``query` is set or empty. But the main issue here is that you're changing the server state on a GET request, which is not correct (the HTTP spec clearly states that a GET request **must** be idempotent) AND a __very__ bad idea anyway (think about what can happen when googlebot crawls your website... there's a reason why GET requests must be idempotent) – bruno desthuilliers Jun 18 '19 at 12:54
  • @brunodesthuilliers what do you think is the best way to implement it, suggestions would be great. – Martins Jun 18 '19 at 13:00
  • Yikes. I really should test stuff before I post it. Yes, .filter() returns a queryset so it would need to loop through the queryset and save, or if there is only 1 result expected, just index it. I get what you mean about the GET request. Good points all. – Deepstop Jun 18 '19 at 13:07
  • @Deepstop you may want to re-read the models doc - there are better ways than looping over the queryset (you can do an update on the queryse as in Ruddra's answert, or in our case - since code is unique - simply use `queryset.get()`. – bruno desthuilliers Jun 18 '19 at 13:27
  • Thanks @brunodesthuilliers I will do that. – Deepstop Jun 18 '19 at 13:30
  • @Martins some questions can hardly be answered without enough context. What is your "Code" object, what is it used for and how, why must it's status be set to false when it's displayed for the first time etc... – bruno desthuilliers Jun 18 '19 at 13:30
  • @brunodesthuilliers Code is a model I want to be valid once then it becomes invalid example is, something like a prepaid recharge pin that can only be recharged once. I just wanted the status field in the database to change after being accessed once. – Martins Jun 18 '19 at 13:39
  • 1
    @Martins "valid once" means it has to be "used" somehow. What defines when a code is used ? NB : I have a couple similar things in our project (giftcards with a code that can only be used once, rebate codes etc), but we do have a clear specification of when of those code is to be considered as "used" - in our case when the user validates his order - which of course happens on a POST request. You say "after being accessed", but how is it accessed ? But anyway, I think you get the point : one way or another, this should only happens on a POST request. – bruno desthuilliers Jun 18 '19 at 14:33