0

I'm new to Django and somewhat to Python as well. I'm trying to find the idiomatic way to loop over a queryset and set a variable on each model. Basically my model depends on a value from an api, and a model method must multiply one of it's attribs by this api value to get an up-to-date correct value.

At the moment I am doing it in the view and it works, but I'm not sure it's the correct way to achieve what I want. I have to replicate this looping elsewhere.

Is there a way I can encapsulate the looping logic into a queryset method so it can be used in multiple places?

NOTE: the variable I am setting on each model instance is just a regular attribute, not saved to db. I just need to be able to set that variable, not save it.

I have this atm (I am using django-rest-framework):

class FooViewSet(viewsets.ModelViewSet):
    model = Foo
    serializer_class = FooSerializer

    bar = # some call to an api

    def get_queryset(self):
        # Dynamically set the bar variable on each instance!
        foos = Foo.objects.filter(baz__pk=1).order_by('date')
        for item in foos:
            item.needs_bar = self.bar

        return items

I would think something like so would be better:

def get_queryset(self):
    bar = # some call to an api
    # Dynamically set the bar variable on each instance!
    return Foo.objects.filter(baz__pk=1).order_by('date').set_bar(bar)

I'm thinking the api hit should be in the controller and then injected to instances of the model, but I'm not sure how you do this. I've been looking around querysets and managers but still can't figure it out nor decided if it's the best method to achieve what I want.

Can anyone suggest the correct way to model this with django?

Thanks.

jmoz
  • 7,846
  • 5
  • 31
  • 33
  • possible duplicate of [How to 'bulk update' with Django?](http://stackoverflow.com/questions/12661253/how-to-bulk-update-with-django) – Hedde van der Heide Nov 01 '13 at 15:51
  • 1
    Not a duplicate, at least not of that question. The OP wants to update the in-memory instances of the models, not the database records. – Peter DeGlopper Nov 01 '13 at 16:16
  • I've just tested the solution in the proposed dupe and it doesn't work: `Item has no field named 'btc_usd'` The field is just an attribute and is not saved to db. – jmoz Nov 01 '13 at 16:26
  • as python is ducky typing, you are already doing it right. As long as need_bar is not an existing attribute. – christophe31 Nov 01 '13 at 16:28
  • There's nothing wrong with doing this in the view, but if you find yourself repeating it you could encapsulate it in a standalone function. I wouldn't make it a method of any class unless it actually depends on that class to determine the `bar` value. – Peter DeGlopper Nov 01 '13 at 16:34

1 Answers1

0

You can set some new properties on queryset items, but they will not update database (will be saved just in local namespace). I suppose that you want to recalculate field of your model multiplying it by some value:

class Foo(models.Model):
     calculated_field = models.BigIntegerField(default=0)

def save(self, *args, **kwargs):
    if self.pk is not None:  # it's not a new record
       foo = kwargs.get('foo')
       if foo:
           self.calculated_field = self.calculated_field * int(foo)
    super(Foo, self).save(*args, **kwargs) # Call the "real" save() method.


 def get_queryset(self):
        bar = # some call to an api
        # Dynamically set the bar variable on each instance!
        foos = Foo.objects.filter(baz__pk=1).order_by('date')
        for item in foos:
            item.save(foo=bar)
       # return updated data
       return Foo.objects.filter(baz__pk=1).order_by('date')

At some point you might need to use transactions if you will run this code simultaneously.

nickzam
  • 793
  • 4
  • 8
  • Sorry forgot to add the field is just am attribute on the model - it's not saved to the db. I'm presuming the `update` hits the db which I don't need. I just need to set a variable on each model which is used in a method. – jmoz Nov 01 '13 at 15:56
  • @nickzam - "You can't just set some new properties on queryset items". Sure you can, they're just Python objects like anything else. The new properties won't be saved to the database, but it works just fine to add arbitrary values to them in memory. – Peter DeGlopper Nov 01 '13 at 16:42
  • @Peter DeGlopper - Thank you, you are right. Edited my answer – nickzam Nov 01 '13 at 16:48