Using a custom manager function in a model definition - django

I'm trying to use manager function to populate a default field of a Model, but I'm having trouble getting it working. Here's my code:
class FooManager(models.Manager):
def foo_filter(self):
return Foo.objects.latest('id')
class Foo(models.Model):
objects = FooManager()
name = models.CharField(
max_length=100,
unique=True,
default=objects.foo_filter())
Whenever I run it,
I get NameError: global name 'Foo' is not defined on the line return Foo.objects.latest('id')

Ha! This is a cool one :)
When your models.py gets loaded for the first time, Python is trying to parse all the class definitions.
So the first thing it sees is FooManager, so far so good.
In your filter method you make use of Foo, which is still unknown at this point. Usually that would not be a problem, because you are using Foo in a method and that method only gets called during runtime, when all the classes are already loaded.
Now Python sees the Foo class and on the name field you are calling one of the manager's methods. At this point, when your filter method is called, the Foo class has not been fully loaded, yet, so when you enter the filter method, Foo is indeed not defined.
You would have to move the manager definition below the Foo definition, but you can't do that, because then the line objects = FooManager() would fail because FooManager would be undefined.
By the way: This is a very common newbie error: You must never put method calls into your field definitions. It means that the method is only executed once when the server starts and Django loads all the model definitions. This leads to extremely frustrating errors:
At first, the default value of your field is indeed the latest Foo object, but when you add a new object to the database, it will not change to the new latest object, it will stay the same object that was the latest when the server was started.
Django usually allows you to put methods into your field definitions, so you could write:
default=objects.foo_filter
without the (). Now, when the server is loaded, the method will not be executed. It will only be executed, when Django actually needs to retrieve the default value, and at that point in time, both, FooManager and Foo have already been loaded.
But apart from that, your manager doesn't even need to know which model it belongs to. Have a look at the Django docs here: https://docs.djangoproject.com/en/1.7/topics/db/managers/#modifying-initial-manager-querysets
Each manager has a get_queryset method which you can override and which you should use, so you could do the following:
class FooManager(models.Manager):
def foo_filter(self):
return self.get_queryset().latest('id')
Now your manager doesn't use the Foo class at all and you won't get into any circular dependency problems.
By the way, I don't think that latest('id') will work, you have to pass in a field name that is a date field, not an ID field.

You are create a loop, that object already is Foo.objects. All you need is:
def foo_filter(self):
return self.latest('id')
On top of that you can't use the database to set defaults for a field like that.

Related

Python/Django model dictionary allows one type of update, but not another

I am working on some Django/Python code.
Basically, the backend of my code gets sent a dict of parameters named 'p'. These values all come off Django models.
When I tried to override them as such:
p['age']=25
I got a 'model error'. Yet, if I write:
p.age=25
it works fine.
My suspicion is that, internally, choice #1 tries to set a new value to an instance of a class created by Django that objects to being overridden, but internally Python3 simply replaces the Django instance with a "new" attribute of the same name ('age'), without regard for the prior origin, type, or class of what Django created.
All of this is in a RESTful framework, and actually in test code. So even if I am right I don't believe it changes anything for me in reality.
But can anyone explain why one type of assignment to an existing dict works, and the other fails?
p is a class, not a dict. Django built it that way.
But, as such, one approach (p.age) lets you change an attribute of the object in the class.

If you have external tasks that gather information about a model should it live in the model as a method?

For example we have a method that fetches additional information about a model from third party APIs. Is it okay to put this as a method on the model or should it live outside?
class Entity(models.Model):
name = ...
location = ...
def fetch_location(self):
# fetch the location from another server and store it.
self.location = "result"
If the data is related to the instance than it can be the right place to put it. Only if you get a lot of these you might want to wrap them in a different class for your own readability (i.e. knowing what is internal and what is external from the instance perspective).
The way I generally do it:
Manager: anything pertaining a group of Model instances
Model: anything pertaining a single Model Instance
Well, if you think in terms of Object Oriented Programming, the answer is "yes":
If "the object can do something", than it should be included as a member function (aka method).
But: If several different classes would need the same functionality (e.g. an "Entity Owner" would like to fetch the location by himself without calling my_entity.fetch_location), you should consider a (abstract) class above both classes which implements the behaviour.
If you have to call the method without an existing instance (which seems not to be the case in your example), you might consider writing the method outside of a class or you add the #staticmethod decorator which allows you to call Entity.fetch_location (remember to omit self in this case since there is no self if there is no instance.) I would prefer the staticmethod over a global method because the caller will always know, which class it relates to.
#staticmethod
def fetch_location():
# fetch the location from another server and store it.
self.location = "result"

Creating a Django callable object for Field.default

I'm trying to create a callable object to return the default value to use for a field when creating a new instance.
The logic for the value is dependent on other data in the model. I tried creating a separate class but have not hit on the right combination of factors. Example:
in models.py:
Class Box(models.Model):
inv_id = models.CharField(max_length=16,default=gen_inv_id())
The callable object will need to query the database model and increment a table value. I tried creating a class in a separate .py module under the app, but it needs a method to return a value. OO is not my strong suit at this point. I think the model has become invalid and the method depends on it so it seems like a chicken/egg scenario has emerged.
Thanks for any help.
Since forever (pre 1.0 days) the default keyword supported callables. The issue with your code is you're not passing in a callable (default=gen_inv_id), but the result of a callable (default=gen_inv_id()).
So you probably want to do:
Class Box(models.Model):
inv_id = models.CharField(max_length=16,default=gen_inv_id)
Check out the docs for the latest version that describes this:
https://docs.djangoproject.com/en/1.4/ref/models/fields/#default
I've run into this before. One thing you can do is to overwrite the class's save method, so that you first save the parameters you need to do the computation, then do the computation and resave. If you're overwriting the save method you'll need to call super.save() (I forget what the exact notation is)
edit: the notation for super.save is super(Model, self).save(*args, **kwargs)

Mutual foo.bar and bar.foo attributes in django Models with onetooneviews

I have two classes, Foo and Bar, in a Django app (on AppEngine if that matters). There's a one to one relationship between them.
class Foo(BaseModel):
bar = Bar.objects.get(foo=self.id)
class Bar(BaseModel):
foo = models.OneToOneField(Foo, blank=True, null=True, help_text="Foos for this bar")
I'd like each object of both classes to have it's related object of the other class as an instance variable.
What's the best way to allow that?
When trying the code above, I'm in an odd situation: since they each refer to each other, I'm trying to use these variables before they're defined (and of course it doesn't work).
I suspect there is A Proper Way to do this, and this isn't it!
You don't need to do anything. This is the default behaviour. Just define the relationship in Bar, and Foo will automatically get a bar attribute.

copy.copy(object) returns an object with id == None during tests?

I am new to Django and Python, building my first app using TDD... I wanted to copy an instance of Task, a model object. I used the following code, which works correctly during the tests:
import copy
class Task(models.Model):
...
def make_copy(self):
new_task = copy.copy(self)
new_task.save()
return new_task
But when running this code 'normally', in the server, I noticed it was not working: no new object was created. I found out I had to add new_task.id = None just before saving, and I understand the reason for this...
But if copy.copy does not know about Django and thus will not change the id itself, why is it the case that the object returned has id == None during the tests?
It sounds like your test case is not fully matching the usage in your "normal" use case.
The id field is set for objects that exist in the database. If you pass your make_copy() method an object with id set, it will appear to fail because it's not making a new database object, it's just saving an existing object (through the Python copy, with an existing id).
I'd guess your test case is passing Task objects to make_copy() with id of None (and therefore appearing to work), while in "normal" usage, the objects are coming in with id set. (You could test this hypothesis with a simple print or assert statement).
One simple solution might be to set id to None right after your copy operation. That way, a new database object is always created.
Finally, someone else with the same situation: http://www.nerdydork.com/copy-model-object-in-django.html