I have two tables (say Parent and Child). I want parent and Child to link to each other using a foreign key. The child table has two fields, one among them being 'country_sig'. This country_sig is actually made from joining two fields in parent table named 'country' and 'code'.
Eg: If country is 'IN' and code is '15' then country_sig is IN.15.
Now I want that during creation of django model for parent table, it should automatically create this field country_sig derived from 'country' and 'code' so that I can reference it to the child table.
PS : Since the parent table is very large, I don't want to create another field in the db derived from those two fields, but I can tweak the other table (child table) to divide the country_sig field into two columns with 'country' and 'code' but that will not work since django does'nt support composite primary key.
EDIT:
Actually I am implementing it in tastypie, I want tastypie to think as if it is a real field. This would solve my real problem.
Perhaps don't use the standard tasypie ModelResources, but write your own resource:
class DictWrapper(object):
'''
to be used as generic tastypie resource object
'''
def __init__(self, initial=None):
self.__dict__['_data'] = {}
if hasattr(initial, 'items'):
self.__dict__['_data'] = initial
def __getattr__(self, name):
return self._data.get(name, None)
def __setattr__(self, name, value):
self.__dict__['_data'][name] = value
def to_dict(self):
return self._data
class ParentResource(Resource):
def get_object_list(self, request):
ret = []
for parent in Parent.objects.all():
ret.append(DictWrapper({"country_sig":"{0}.{1}".format(parent.country, parent.code})))
return return
def obj_get_list(self, request=None, **kwargs):
return self.get_object_list(request)
Related
Currently in a situation where I have a series of classes that turn an API request JSON into objects. The objects are modeled after my database schema. I think the part that I'm struggling with is how to represent those entity relationships that are formed with foreign keys in my database.
The following classes are just for an example, the instance variables are much different for my application's schema.
class Table(ABC):
def __init__(self):
# stuff
#abstractmethod
def validateSchema(self):
"""Validates the resources column values."""
pass
class ClassRoom(Table):
def __init__(self, id, location_id, location):
super().__init__()
self.id = id
self.location = Location(location_id, location)
def validateSchema(self):
# stuff
class Location(Table):
def __init__(self, id, location):
super().__init__()
self.id = id
self.location = location
def validateSchema(self):
# stuff
The part I'm concerned about is when I am creating an object of the same type as the class that has the object as an instance variable.
class ClassRoom(Table):
def __init__(self, id, location_id, location):
# Can I instantiate this class even if it inherits the same parent?
self.location = Location(location_id, location)
Is this ok in OOP? Is there a better way to design my classes?
Also, these classes are just defined for the request JSONs that get sent to my API. Their purpose will be to facilitate column validation and a few other purposes. The specific validation I am hoping to implement in these classes comes from this other Stackoverflow post Flask sqlAlchemy validation issue with flask_Marshmallow. I'm not trying to recreate SqlAlchemy here.
Your Table class is analogous to SqlAlchemy's db.Model class. And just as it can have references between different subclasses, so can you.
The specific design of your Classroom.__init__() method seems wrong. All the classrooms in the same location should have references to the same Location object, but you create a new one for each classroom. The Location should be a parameter, rather than the location ID and name.
class ClassRoom(Table):
def __init__(self, id, location):
super().__init__()
self.id = id
self.location = location
Then you can create multiple classrooms in a location:
loc = Location(loc_id, loc_name)
c1 = Classroom(c1_id, loc)
c2 = Classroom(c2_id, loc)
I am working in Python 2.7 and trying to create a model structure which is something similar to django models but without database. Consider the following situation:
I want to create a model for Products (e.g CarryBag) which will have some predefined attributes of basic type (string, int, boolean etc), using which instances can be created. This model must inherit from an abstract model say 'GenericProduct' which will have certain defined functionality related to a product:
class GenericProduct:
name = StringType(required=True)
id = StringType(required=True)
def to_json(self):
....
def to_xml(self):
...
def some_other_function(self):
...
class CarryBag(GenericProduct):
material = StringType(required=True, default="Polyester", help_text="Type of material used")
price = IntType(required=True)
available = BooleanType(default=False)
So, challenges are:
This modelling structure must work very much similar to Django models but without database interaction.
How do I define abstract class 'GenericProduct'
How do I create a class that defines datatype classes 'IntType', 'StringType' & 'BooleanType' which can be used to define attributes.
What is the best way to achieve this keeping in mind python constructs. I am trying to go through django db model code to get some sense about it. Following is my code for one of the datatype classes:
class StringType(object):
def __init__(self, default=None, required=False, help_text=None):
self.required = required
self.help_text = help_text
if default:
self.__set__(self,default)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError("'{}' must be a string.".format(value))
self.value = value
And this is how I was using it:
class GenericProduct:
name = StringType(required=True) # WRONG: This creates a static member of the class
.....
I have two simple models with ForeignKey relation, Category and Object let say (Object has FK attribute to Category) and in administration I need to assign value of another object attribute value base on the actually selected Category.
Example:
I will create in Django Admin interface Category with attribute cat_name="A" and another Category with cat_name="B".
Now in the Object creation form I can select in the form "A" or "B" Category, and based on that selection I need to store in Object.description attribute something like "Selected category is B"
I've tried several approaches but all ended on the fact that the instance of the Category object has to be somehow passed to the Object creation form.
Thanks
You do this before saving you data by overriding the save function:
class ObjectModel(models.Model):
category = models.ForeignKey(# details goes here)
..... # other fields goes here
def save(self, *args, **kwargs):
if self.category.name == 'A':
self.description = ...
elif self.category.name == 'B':
.... # different behavior etc
super(ObjectModel, self).save(*args, **kwargs)
OK, finaly I have find acceptable solution. I'm creating the related object at the time of Category object is being saved and passing it's attribute to the object. Something like
class Category(models.Model):
name = models.CharField(max_length=10)
...
def save(self, *args, **kwargs):
super(Category, self).save(*args, **kwargs)
Object.objects.create(name=self.name)
Only disadvantage I've noticed that Objects created this automated way has to have empty (if allowed) or machine generated attributed, but that's just a minor defect for me and I can do any updates via common administration form is needed.
So far I'm extremely happy with Django Rest Framework, which is why I alsmost can't believe there's such a large omission in the codebase. Hopefully someone knows of a way how to support this:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True, source='item')
item = serializers.IntegerSerializer(write_only=True)
class Meta:
model = Pin
with the goal
The goal here is to read:
{pin: item: {name: 'a', url: 'b'}}
but to write using an id
{pin: item: 10}
An alternative would be to use two serializers, but that looks like a really ugly solution:
django rest framework model serializers - read nested, write flat
Django lets you access the Item on your Pin with the item attribute, but actually stores the relationship as item_id. You can use this strategy in your serializer to get around the fact that a Python object cannot have two attributes with the same name (a problem you would encounter in your code).
The best way to do this is to use a PrimaryKeyRelatedField with a source argument. This will ensure proper validation gets done, converting "item_id": <id> to "item": <instance> during field validation (immediately before the serializer's validate call). This allows you to manipulate the full object during validate, create, and update methods. Your final code would be:
class PinSerializer(serializers.ModelSerializer):
item = ItemSerializer(read_only=True)
item_id = serializers.PrimaryKeyRelatedField(write_only=True,
source='item',
queryset=Item.objects.all())
class Meta:
model = Pin
fields = ('id', 'item', 'item_id',)
Note 1: I also removed source='item' on the read-field as that was redundant.
Note 2: I actually find it rather unintuitive that Django Rest is set up such that a Pin serializer without an Item serializer specified returns the item_id as "item": <id> and not "item_id": <id>, but that is beside the point.
This method can even be used with forward and reverse "Many" relationships. For example, you can use an array of pin_ids to set all the Pins on an Item with the following code:
class ItemSerializer(serializers.ModelSerializer):
pins = PinSerializer(many=True, read_only=True)
pin_ids = serializers.PrimaryKeyRelatedField(many=True,
write_only=True,
source='pins',
queryset=Pin.objects.all())
class Meta:
model = Item
fields = ('id', 'pins', 'pin_ids',)
Another strategy that I previously recommended is to use an IntegerField to directly set the item_id. Assuming you are using a OneToOneField or ForeignKey to relate your Pin to your Item, you can set item_id to an integer without using the item field at all. This weakens the validation and can result in DB-level errors from constraints being violated. If you want to skip the validation DB call, have a specific need for the ID instead of the object in your validate/create/update code, or need simultaneously writable fields with the same source, this may be better, but I wouldn't recommend anymore. The full line would be:
item_id = serializers.IntegerField(write_only=True)
If you are using DRF 3.0 you can implement the new to_internal_value method to override the item field to change it to a PrimaryKeyRelatedField to allow the flat writes. The to_internal_value takes unvalidated incoming data as input and should return the validated data that will be made available as serializer.validated_data. See the docs: http://www.django-rest-framework.org/api-guide/serializers/#to_internal_valueself-data
So in your case it would be:
class ItemSerializer(ModelSerializer):
class Meta:
model = Item
class PinSerializer(ModelSerializer):
item = ItemSerializer()
# override the nested item field to PrimareKeyRelatedField on writes
def to_internal_value(self, data):
self.fields['item'] = serializers.PrimaryKeyRelatedField(queryset=Item.objects.all())
return super(PinSerializer, self).to_internal_value(data)
class Meta:
model = Pin
Two things to note: The browsable web api will still think that writes will be nested. I'm not sure how to fix that but I only using the web interface for debug so not a big deal. Also, after you write the item returned will have flat item instead of the nested one. To fix that you can add this code to force the reads to use the Item serializer always.
def to_representation(self, obj):
self.fields['item'] = ItemSerializer()
return super(PinSerializer, self).to_representation(obj)
I got the idea from this from Anton Dmitrievsky's answer here: DRF: Simple foreign key assignment with nested serializers?
You can create a Customized Serializer Field (http://www.django-rest-framework.org/api-guide/fields)
The example took from the link:
class ColourField(serializers.WritableField):
"""
Color objects are serialized into "rgb(#, #, #)" notation.
"""
def to_native(self, obj):
return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue)
def from_native(self, data):
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
return Color(red, green, blue)
Then use this field in your serializer class.
I create a Field type that tries to solve the problem of the Data Save requests with its ForeignKey in Integer, and the requests to read data with nested data
This is the class:
class NestedRelatedField(serializers.PrimaryKeyRelatedField):
"""
Model identical to PrimaryKeyRelatedField but its
representation will be nested and its input will
be a primary key.
"""
def __init__(self, **kwargs):
self.pk_field = kwargs.pop('pk_field', None)
self.model = kwargs.pop('model', None)
self.serializer_class = kwargs.pop('serializer_class', None)
super().__init__(**kwargs)
def to_representation(self, data):
pk = super(NestedRelatedField, self).to_representation(data)
try:
return self.serializer_class(self.model.objects.get(pk=pk)).data
except self.model.DoesNotExist:
return None
def to_internal_value(self, data):
return serializers.PrimaryKeyRelatedField.to_internal_value(self, data)
And so it would be used:
class PostModelSerializer(serializers.ModelSerializer):
message = NestedRelatedField(
queryset=MessagePrefix.objects.all(),
model=MessagePrefix,
serializer_class=MessagePrefixModelSerializer
)
I hope this helps you.
I was going through code of https://github.com/hit9/CURD.py/blob/master/CURD.py which is a simple orm that performs normal curd operations .. and i could not understood part of code which goes like this(on line number 616):
.....#smthing #..
for name, attr in cls.__dict__.iteritems():
if isinstance(attr, Field):
attr.describe(name, cls)
fields[name] = attr
what does attr.describe(attr, Field) do ? I googled it out but found nothing.
It's not a Python language feature, it's a method on that library. You can see the definition here:
https://github.com/hit9/CURD.py/blob/master/CURD.py#L251
class Field(Leaf):
"""
Field object.
Field examples: User.name, User.age ..
"""
def __init__(self, is_primarykey=False, is_foreignkey=False):
self.is_primarykey = is_primarykey
self.is_foreignkey = is_foreignkey
# describe model's attr
def describe(self, name, model):
self.name = name
self.model = model
# fullname e.g. : User.id 's fullname is "user.id"
self.fullname = self.model.table_name + "." + self.name
# describe the attribute, reload its access control of writing, reading
setattr(model, name, FieldDescriptor(self))
setattr sets an attribute on an object. So if I call describe("field_name", myObject), it will set myObject.field_name to the description of the model. Or something like that.
That is not a python standard thing.
The loop iterates through the names and values of a class, and the attributes of that class that are instances of a Field type are added to a dictionary.
Field is not part of python standard library, you should search that project for the Field class.