Representing a set in a Django model - django-models

I have a use case when a Django model class can have any of a specified set of values. If it weren't a Django model, I'd do something like:
attr_list = ['green', 'blue', 'fuzzy', 'smooth']
class my_model:
def __init__(self, attrs=None):
self._attrs = set(attrs)
def quality(self, attr):
return attr in self._attrs
obj = my_model(['green', 'fuzzy'])
if obj.quality('green'):
print("It's green")
But in Django, the only options seem to be a class that represents the link between the model and the quality, with a class that just has one attribute holding the quality value:
class QualityLink(models.Model):
quality = models.CharField(
choices=[('green', 'Green'), ...]
...)
parent = models.ForeignKey(MyModel, ...)
or, if I want to avoid the overhead of a bunch of database accesses, unwinding the values:
class MyModel(models.Model):
is_green = models.BooleanField(...)
is_blue = models.BooleanField(...)
is_fuzzy = models.BooleanField(...)
is_smooth = models.BooleanField(...)
Neither of these seems like a very good approach. The first seems inefficient and the second way too clunky. Am I missing something?

Related

Serializer field for side effect model django rest framework

I have a django.db.models.Model A whose instances are created in a rest_framework.serializers.ModelSerializer from POST requests.
Depending on the data being sent in the POST, I would like to create one of several other "addon" models, let's say B or C, which I link to the original through a django.db.models.OneToOneField:
from django.db import models
class A(models.Model):
some_field = models.CharField()
class B(models.Model):
a = models.OneToOneField(A, related_name='addon', on_delete=models.CASCADE)
class C(models.Model):
a = models.OneToOneField(A, related_name='addon', on_delete=models.CASCADE)
What I would like to is to have a serializer which validates the incoming data, including some string indicating which addon to use. The serializer then creates the model instance of A and based on this creates the addon model.
I do not want to create a utility field in model A used to determine which addon to use, I would like to create the model directly using the instance of model A and information from the POST itself.
At the same time when accessing the data through a get, I would like to return the original string used to determine which addon to use.
What I have come up with so far:
from rest_framework import serializers
str2model = {'b': B, 'c': C}
class AddonField(serializers.Field):
def to_representation(self, value):
# I completely ignore "value" as no "internal value" is set in "to_internal_value"
myvalue = self.parent.instance.addon
for addon_name, addon_class in str2model.items():
if isinstance(myvalue, addon_class):
return addon_name
def to_internal_value(self, data):
# I create the "internal value" after "A" instance is created, thus here I do nothing?
return data
class ASerializer(serializers.ModelSerializer):
some_field = serializers.CharField()
the_addon = AddonField()
def validate_the_addon(self, value): # here addon is a string
if value in str2model.keys():
return value
def create(self, validated_data):
addon_name = validated_data.pop('the_addon')
addon_class = str2model[addon]
a = super(ASerializer, self).create(validated_data)
addon_class.objects.create(a=a)
return a
class Meta:
model = A
fields = ["some_field", "the_addon"]
When testing this I get:
AttributeError: Got AttributeError when attempting to get a value for field `the_addon` on serializer `ASerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `A` instance.
Original exception text was: 'A' object has no attribute 'the_addon'.
How can I temporarily store the_addon in the serializer until the A instance has been created?
This is how I would typically approach it
# Serializer
class ASerializer(serializers.Serializer):
some_field = serializers.CharField()
addon_b = serializers.CharField(required=False, allow_null=True)
addon_c = serializers.CharField(required=False, allow_null=True)
def create(self, validated_data):
addon_b = validated_data.pop('addon_b')
addon_c = validated_data.pop('addon_c')
a = A.objects.create(some_field=validated_data['some_field'])
if addon_b:
B.objects.create(a=a)
if addon_c:
C.objects.create(a=a)
return a
You can do other validations if necessary.
class TestAPIView01(generics.CreateAPIView):
permission_classes = {}
serializer_class = serializers.ASerializer
queryset = A.objects.all()
Also, look at the related_name on B and C you may want to consider making them different, as that might throw an error in the future. Cheers

Problems with StructuredProperty and StringProperty

i am doing the finally degree work in Google App Engine, but i am having problems when i try this:
class Predicate(ndb.Model):
name = ndb.StringProperty()
parameters = ndb.JsonProperty()
class State(ndb.Model):
predicates = ndb.StructuredProperty(Predicate, repeated=True)
class Action(ndb.Model):
name = ndb.StringProperty()
parameters = ndb.StringProperty(repeated=True)
preconditions = ndb.StructuredProperty(Predicate, repeated=True)
predicatesToAdd = ndb.StructuredProperty(Predicate, repeated=True)
predicatesToDel = ndb.StructuredProperty(Predicate, repeated=True)
class Plan(ndb.Model):
plan = ndb.StructuredProperty(Predicate, repeated=True)
class Problem(ndb.Model):
initialState = ndb.StructuredProperty(Predicate)
goalState = ndb.StructuredProperty(Predicate)
actions = ndb.StructuredProperty(Action, repeated=True)
i get this error:
TypeError: This StructuredProperty cannot use repeated=True because its model class (Predicate) contains repeated properties (directly or indirectly).
StructuredProperty, if it contains repetitions, can not be replicated another StructuredProperty. But I need this structure models. How can i solve this?
And sorry for my bad english :(
I solved this problem using LocalStructuredProperty, but I think it will not work at all
The problem with your design is that ndb does not allow nested repeated properties. In other words, you cannot have a repeated structured property, which in turn has its own repeated property. If you remove the repeated=True from the parameters property, it will work.
You will need to re-think your design to work around this. One possible solution may be to use a JsonProperty for parameters, and store the list of strings as a JSON string. You won't be able to query them then of course, but it may work out depending on your requirements.

Using a Model with "general purpose" fields to model inheritance

I am currently kidding around on how to model something using Django. The basic situation is that I have an object that should serve as a chassis and provide some sockets. Then there are loads of different modules that are placed on the sockets of a chassis. I would like to model these different modules as distinct classes in Django, but use a common class on the database layer, possibly involving some generic fields.
So the database model might look something like this:
class Module(models.model):
name = models.CharField(max_length=128)
# Or is there a better way to annotate a type?
type = models.CharField(max_length=128)
string1 = models.CharField(max_length=128)
string2 = models.CharField(max_length=128)
...
int1 = models.IntegerField()
# Some kind of engine class that derives from Module
# but does nothing else then "mapping" the generic
# fields to something sensible
class Socket(models.Model):
is_on = models.ForeignKey(Chassis)
name = models.CharField(max_length=128)
type = models.CharField(max_length=128)
class Connection(models.Model):
chassis = models.ForeignKey(Chassis)
module = models.ForeignKey(Module)
via = models.ForeignKey(Socket)
class Chassis(models.Model):
name = models.CharField(max_length=128)
modules= models.ManyToManyField(Module, through='Connection')
class Group(models.Model):
name = models.CharField(max_length=128)
Of course I wouldn't want to spoil my logic with this denormalization. Thats why I hinted for some kind of engine class that should use the Module table, but provide "logical" getters and setters, effectively mapping data like "Horsepower" to "int1".
So my questions are basicly:
Is what I am doing reasonable with Django? Or is there a better (possibly built in) way to deal with this?
Would it be possible to construct the correct type, the one providing the wrapper methods for the denormalized model, depending on the Module.type field automatically?
Is what I am doing reasonable with Django?
The general idea is okay, but the denormalization may make your queries less than optimal. The standard solution would be to subclass Module for each type of module; this would create a module table plus a table per module type with the type-specific stuff. This, of course, assumes you won't be creating or deleting module types at runtime.
That said, there are some issues with your models:
class Module(models.model):
name = models.CharField(max_length=128)
# Or is there a better way to annotate a type?
type = models.CharField(max_length=128)
string1 = models.CharField(max_length=128)
string2 = models.CharField(max_length=128)
...
int1 = models.IntegerField()
Normally, type would get normalized to save space:
class ModuleType(models.model):
name = models.CharField(max_length=128)
# Any other type-specific parameters.
class Module(models.model):
name = models.CharField(max_length=128)
type = models.ForeignKey(ModuleType, related_name="modules")
string1 = models.CharField(max_length=128)
string2 = models.CharField(max_length=128)
...
int1 = models.IntegerField()
class Socket(models.Model):
is_on = models.ForeignKey(Chassis)
name = models.CharField(max_length=128)
type = models.CharField(max_length=128)
class Connection(models.Model):
chassis = models.ForeignKey(Chassis)
module = models.ForeignKey(Module)
via = models.ForeignKey(Socket)
class Chassis(models.Model):
name = models.CharField(max_length=128)
sockets = m
modules= models.ManyToManyField(Model, through='Socket')
Chassis is a mess. You didn't define sockets, wrote Model where you probably want module, and through should probably refer to Connection (a through model has to have ForeignKeys to both ends of the link). But from your description, I get the far simpler:
class Socket(models.Model):
chassis = models.ForeignKey(Chassis, related_name="sockets")
name = models.CharField(max_length=128)
# Is `type` a ModuleType? If so, use a ForeignKey.
# If not, create a SocketType model.
type = models.___
module = models.ForeignKey(Module, related_name="sockets")
class Chassis(models.Model):
name = models.CharField(max_length=128)
sockets = models.IntegerField()
modules = models.ManyToManyField(Socket)
With a better description of what you're modeling, this can be refined further. For example, I'm not sure a ManyToMany is what you want. You may need to split the design of a chassis (i.e. things common to all instances of a given kind of chassis, including its sockets) from the instances of that chassis (which would reference the design, and have another table mapping sockets to modules).
Would it be possible to construct the correct type depending on the Module.type field automatically?
That's the Factory design pattern. In Python, you'd implement it as a dictionary of constructors:
class Module(models.model):
# ...
CONSTRUCTORS = {}
#staticmethod
def register_constructor(type_name, constructor):
Module.CONSTRUCTORS[type_name] = constructor
def construct(self):
return Module.CONSTRUCTORS[self.type.name](self)
I don't think you need a specific engine class; the various module classes will suffice.
In order to define an abstract base class you can do the following:
class Module(models.model):
name = models.CharField(max_length=128)
type = models.CharField(max_length=128)
string1 = models.CharField(max_length=128)
string2 = models.CharField(max_length=128)
...
int1 = models.IntegerField()
class Meta:
abstract = True
class SpecificModule(Module):
subClassField = models.CharField(max_length=128)
I would recommend to read this part of the documentation as it is a perfectly good starting point for dealing with inheritance and abstract classes.
You could also define the parent Module class without class Meta: abstract = True . The only difference is that with abstract = True all fields of the class SpecificModule and all the fields of the abstract Module parent class will be created in the subclass table for the class SpecificModule, whereas with no abstract = True definition a table for the class Module will be created, having all "generic" fields available in the Module table, and all sub-class specific fields in the table for the class SpecificModule.
Edit: In reply to the question regarding relationship between parent and child.
As an implicit one-to-one field is created in the subclass table (docs), you can get the child object using this query.
#get the parent
parent = Module.objects.get(name="my first module")
#this is possible
print parent.name
>>> "my first module"
#this is not possible
print parent.subClassField
>>> Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Module' object has no attribute 'subClassField'
#get the corresponding child
child = SpecificModule.objects.get(module_ptr_id=parent.pk)
#finally we can print the value
print child.subClassField
I think there should be also a default related_name be created by Django, but not sure which one it is.
Please note that an implicit one-to-one relationship from parent to child is only created in the abstract=False case. Nevertheless if abstract = True you will not have the abstract parent available as an object, since it is abstract..

Django, autogenerate one-to-many tables, and database structure

I am using django for a website where I have a database with users, people, locations, items and so on. Know i find that I need some extra information that requires one-to-many relations like Aliases for most of these tables.
Should I (1) create a common alias table for all of these by using the content type framework (will probably end up with billions of rows), or should I (2) create a alias table for each of these. If the latter one, how do I auto-create one-to-many table like this by just adding a single line like this
"alias = Ailias()"
in each model. I`m sure I saw an app doing something like that way a while ago, I think is was a reversion app of some kind. Even if the second method is not suited i would love tho understand how to do it. I do not know what to search after to find an explanation of this.
I plan to add Haystack with Solr to this, so method 2 might add much extra work there. But I do not have much experience with it jet, so I might be wrong.
PS: ended up wih method one.
Manage to do what I wanted in method 2, easily generate one-to-many fields. Not sure if this is the easiest way, or the best way. If someone has a better way of doing it, I would love to learn it. I am a long way from a django expert, so I might have meddled with some unnecessary complex stuff to do what I wanted.
This example creates an easy way of adding a one-to-many alias relationship.
Alias Managers
class AliasManagerDescriptor(object):
def __init__(self, model,fkName):
self.model = model
self.fkName = fkName
def __get__(self, instance, owner):
if instance is None:
return AliasManager(self.model,self.fkName)
return AliasManager(self.model, self.fkName, instance)
class AliasManager(models.Manager):
def __init__(self, model,fkName, instance=None):
super(AliasManager, self).__init__()
self.model = model
self.instance = instance
#Name of FK linking this model to linked model
self.fkName=fkName
def get_query_set(self):
"""
Get query set, or only get instances from this model that is linked
to the chosen instance from the linked model if one is chosen
"""
if self.instance is None:
return super(AliasManager, self).get_query_set()
if isinstance(self.instance._meta.pk, models.OneToOneField):
#TODO: Checkif this part works, not checked
filter = {self.instance._meta.pk.name+"_id":self.instance.pk}
else:
filter = {self.fkName: self.instance.pk}
return super(AliasManager, self).get_query_set().filter(**filter)
def create(self,**kwargs):
"""
Create alias instances. If FK is not given then it is automatically set
to the chosen instance from the linked model
"""
if self.fkName not in kwargs:
kwargs[self.fkName]=self.instance
print kwargs
super(AliasManager, self).create(**kwargs)
Alias Models
class Alias(object):
def contribute_to_class(self, cls, name):
self.manager_name = name
aliasModel = self.create_alias_model(cls)
descriptor = AliasManagerDescriptor(aliasModel,cls._meta.object_name.lower())
setattr(cls, self.manager_name, descriptor)
def create_alias_model(self, model):
"""
Creates a alias model to associate with the model provided.
"""
attrs = {
#'id': models.AutoField(primary_key=True),
"name": models.CharField(max_length=255),
#Not sure which to use of the two next methods
model._meta.object_name.lower(): models.ForeignKey(model),
#model._meta.object_name.lower(): AliasObjectDescriptor(model),
'__unicode__': lambda self: u'%s' % self.name,
'__module__': model.__module__
}
attrs.update(Meta=type('Meta', (), self.get_meta_options(model)))
name = '%s_alias' % model._meta.object_name
return type(name, (models.Model,), attrs)
def get_meta_options(self, model):
"""
Returns a dictionary of fields that will be added to
the Meta inner class.
"""
return {
}
"""class AliasObjectDescriptor(object):
def __init__(self, model):
self.model = model
def __get__(self, instance, owner):
values = (getattr(instance, f.attname) for f in self.model._meta.fields)
return self.model(*values)"""
Person Model - Only need to add "alias = Alias()" to a model to add a one-to-many alias field.
class Person(models.Model):
name = models.CharField(max_length=30,blank=True,null=True)
age = models.IntegerField(blank=True,null=True)
alias = Alias()
Now you I can do something like this:
per = Person(name="Per",age=99)
per.save()
per.alias.create(name="Mr.P")
per_alias = per.alias.all().values_list("name",flat=True)

Django models, custom functions

I am creating simple application with django. Also, I realized that I am doing some kind of operations very often. For example I often need to get all Article objects which have isPublick = True. So I am thinking is that possible to define get_published function in model?
if models looks like this (simplified)
class Article(models.Model):
title = models.CharField(...)
isPublished = models.BooleandField()
def get_active(self):
return Article.objects.filter(isPublicshed = 1)
But it doesn't work this way
Can you suggest a way of implementing the function?
What you probably want is a custom manager
From the django docs:
# An example of a custom manager called "objects".
class PersonManager(models.Manager):
def get_fun_people(self):
return self.filter(fun=True)
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
fun = models.BooleanField()
objects = PersonManager()
def __unicode__(self):
return u"%s %s" % (self.first_name, self.last_name)
which then allows you to do something like:
>>> p1 = Person(first_name='Bugs', last_name='Bunny', fun=True)
>>> p1.save()
>>> p2 = Person(first_name='Droopy', last_name='Dog', fun=False)
>>> p2.save()
>>> Person.objects.get_fun_people()
[<Person: Bugs Bunny>]
As is stated in the docs here, if you need to add custom row-level functionality to your objects, you need to define custom methods on your models. However, if what you are after is custom table-wide functionality (such as getting all Article objects that qualify certain conditions), you have to define custom methods on model Managers (much as aciniglio above points out in their answer).
You can use the staticmethod decorator.
class Article(models.Model):
title = models.CharField(...)
isPublished = models.BooleandField()
#staticmethod
def get_active():
return Article.objects.filter(isPublished = 1)

Categories

Resources