I have plenty of Hardware
models which have a HardwareType
with various characteristics. Like so:
# models.py
from django.db import models
class HardwareType(model.Models):
name = models.CharField(max_length=32, unique=True)
# some characteristics of this particular piece of hardware
weight = models.DecimalField(max_digits=12, decimal_places=3)
# and more [...]
class Hardware(models.Model):
type = models.ForeignKey(HardwareType)
# some attributes
is_installed = models.BooleanField()
location_installed = models.TextField()
# and more [...]
If I wish to add a new Hardware
object, I first have to retrieve the HardwareType
every time, which is not very DRY:
tmp_hd_type = HardwareType.objects.get(name='NG35001')
new_hd = Hardware.objects.create(type=tmp_hd_type, is_installed=True, ...)
Therefore, I have tried to override the HardwareManager.create()
method to automatically import the type when creating new Hardware
like so:
# models.py
from django.db import models
class HardwareType(model.Models):
name = models.CharField(max_length=32, unique=True)
# some characteristics of this particular piece of hardware
weight = models.DecimalField(max_digits=12, decimal_places=3)
# and more [...]
class HardwareManager(models.Manager):
def create(self, *args, **kwargs):
if 'type' in kwargs and kwargs['type'] is str:
kwargs['type'] = HardwareType.objects.get(name=kwargs['type'])
super(HardwareManager, self).create(*args, **kwargs)
class Hardware(models.Model):
objects = HardwareManager()
type = models.ForeignKey(HardwareType)
# some attributes
is_installed = models.BooleanField()
location_installed = models.TextField()
# and more [...]
# so then I should be able to do:
new_hd = Hardware.objects.create(type='ND35001', is_installed=True, ...)
But I keep getting errors and really strange behaviors from the ORM (I don't have them right here, but I can post them if needed). I've searched in the Django documentation and the SO threads, but mostly I end up on solutions where:
Hardware.save()
method is overridden (should I get the HardwareType
there ?) or,create_something
method which calls self.create()
.I also started digging into the code and saw that the Manager
is some special kind of QuerySet
but I don't know how to continue from there. I'd really like to replace the create
method in place and I can't seem to manage this. What is preventing me from doing what I want to do?
Whenever one tries to create an instance of a model either from admin interface or django shell, save() function is run. We can override save function before storing the data in the database to apply some constraint or fill some ready only fields like SlugField.
def __str__( self ): return "something" This will display the objects as something always in the admin interface. Most of the time we name the display name which one could understand using self object. For example return self.
To create a new instance of a model, instantiate it like any other Python class: class Model (**kwargs) The keyword arguments are the names of the fields you've defined on your model. Note that instantiating a model in no way touches your database; for that, you need to save() .
The insight from Alasdair's answer helped a lot to catch both strings and unicode strings, but what was actually missing was a return
statement before the call to super(HardwareManager, self).create(*args, **kwargs)
in the HardwareManager.create()
method.
The errors I was getting in my tests yesterday evening (being tired when coding is not a good idea :P) were ValueError: Cannot assign None: [...] does not allow null values.
because the subsequent usage of new_hd
that I had create()
d was None
because my create()
method didn't have a return
. What a stupid mistake !
Final corrected code:
class HardwareManager(models.Manager):
def create(self, *args, **kwargs):
if 'type' in kwargs and isinstance(kwargs['type'], basestring):
kwargs['type'] = HardwareType.objects.get(name=kwargs['type'])
return super(HardwareManager, self).create(*args, **kwargs)
Without seeing the traceback, I think the problem is on this line.
if 'type' in kwargs and kwargs['type'] is str:
This is checking whether kwargs['type']
is the same object as str
, which will always be false.
In Python 3, to check whether `kwargs['type'] is a string, you should do:
if 'type' in kwargs and isinstance(kwargs['type'], str):
If you are using Python 2, you should use basestring
, to catch byte strings and unicode strings.
if 'type' in kwargs and isinstance(kwargs['type'], basestring):
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With