I'm trying to understand, if it is possible to inherit a regular python class in a django model. The idea is that I need a common interface for the models and for another part of the system(that works with mongo).
The example is:
class myC(object):
def __init__(self):
self.f = "test"
class myM(myC, models.Model):
name = models.CharField(max_length=50)
Making myC inherit from Model and making it abstract is not really possible for me and even more. So I wonder, what would the table for myM look like(if what I am trying to do is possible).
Yes you could inherit your models from regular classes. For example create some mixins for your models.
This mixin will add missing / to url field each time you save a model:
class ModelURLSaveMixin(object):
def save(self, *args, **kwargs):
if not self.url.startswith('/'):
self.url = '/' + self.url
if not self.url.endswith('/'):
self.url = self.url + '/'
super(ModelURLSaveMixin, self).save(*args, **kwargs)
class MyModelWithUrlField(ModelURLSaveMixin, models.Model):
...
But if you want build common interface for several models on your project an abstract class solution will be better
Related
I have an abstract mixin class that adds a Django model field to any concrete class that inherits from it.
At class initialisation - when makemigrations is run - I'd like the inheriting class to define whether an inherited field is required or optional via the blank= True or False property.
I've tried various Meta and __new__ approaches, but can't figure out how the abstract mixin class can get the information from the inheriting class.
Here's a naive attempt:
from django.db import models
class DescriptionMixin(models.Model):
class Meta:
abstract = True
description = models.TetxField(
# how to get value here?
blank=inheriting_class.description_required
)
class OptionalDescription(DescriptionMixin, SomeOtherClass):
class Meta:
verbose_name = 'Optional description'
description_required = False
class RequiredDescription(DescriptionMixin, SomeOtherClass):
class Meta:
verbose_name = 'Required description'
description_required = True
Thanks in advance for any help offered.
You can't do this at the database level. makemigrations doesn't actually initialise your models to create the migration files.
But since you're trying to enforce this on a TextField, which cannot be enforced at the database level anyway (blank is only used when validating a model through the full_clean() method), you could just override the clean() method on the DescriptionMixin, checking the value of self.blank and raising a ValidationError appropriately.
Solved using this (it's actually Wagtail on top of Django):
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)
self._meta.get_field('description').blank = not getattr(self, 'description_required', False)
Using Django 1.11 I'd like to make a custom model field that inherits from ForeignKey.
With a normal ForeignKey, you can do something like the following:
class Car(models.Model):
manufacturer = models.ForeignKey(Company)
Instead, I'd like to make a model field that is largely the same as the ForeignKey field, but 1) uses a different default widget for the formfield and 2) doesn't require the model name to be placed as a positional parameter.
# myapp/models.py
from otherapp.fields import ManufacturerField
class Car(models.Model):
manufacturer = ManufacturerField()
Unfortunately, I'm having a hard time overriding the init method of the child class to get my "Company" model inserted into the mix. Here's what I have so far by way of a modelfield (not working on the widget at all yet):
# otherapp/fields.py
from otherapp.models import Company
class ManufacturerField(models.ForeignKey):
def __init__(self, *args, **kwargs):
return super(ContentField, self).__init__(Company, **kwargs)
When I try to do this, I get:
TypeError: Couldn't reconstruct field manufacturer on myapp.Car: __init__() got multiple values for argument 'to'
Is there a property I can set on the custom modelfield class to specify that I want this to be a foreignkey to one specific model? If not, any ideas on how I can properly intercept the init method to feed in my model?
super() referring to the base class explicitly. you can see this question to understand it.
def __init__(self, *args, **kwargs):
return super(ContentField, self).__init__(Company, **kwargs)
should be;
def __init__(self, *args, **kwargs):
return super(ManufacturerField, self).__init__(*args, **kwargs)
Here is an example how to custom the field.
Some Context
I have the following django/python snippet:
from rest_framework import serializers
from .models import Profile, Task
class Serializable():
types = {}
def __init__(self, objectid):
self.object = self.types[objectid][0]
self.serializer = self.types[objectid][1]
def serialized(self):
instances = self.object.objects.all()
serialized = self.serializer(instances, many=True)
return serialized
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
oid = 'profile'
model = Profile
fields = ['login', 'status']
Serializable.types[oid] = [model, <class-reference>]
class TaskSerializer(serializers.ModelSerializer):
class Meta:
oid = 'task'
model = Task
fields = ['description', 'date', 'owner']
Serializable.types[oid] = [model, <class-reference>]
I am using Django with the rest_framework library installed. One of the interesting features I am using is ModelSerializers (ModelSerializers Documentation), which save quite a lot of code repetition. I want Serializable.types variable to be populated on runtime (when all the serializer classes are declared). The whole point of this is that I will not have to update my views whens a new type of model is included. For example, I would print the json representation of my model instances like this:
class QueryObject(APIView):
permission_classes = (AllowAny,)
def get(self, request, *args, **kwargs):
oid = request.GET['oid']
serializable= Serializable(oid)
json = serializable.serialized
return JsonResponse(json)
The Problem
The major problem is in the last line of each Serializer class.
Serializable.types[oid] = [model, <class-reference>]
I've tried putting the name of the class, ProfileSerializer for example, to no avail. I've tried doing the same outside of the Meta class, such as:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
oid = 'profile'
model = Profile
fields = ['login', 'status']
Serializable.types[Meta.oid] = [Meta.model, ProfileSerializer]
also not successful. Not sure what else to do, which is why I'm hoping the community can help me on this one.
This is actually a case for defining a metaclass.
I've never actually found a source of information which gives a complete, clear and satisfactory explanation as to what metaclasses are or how they work. I will try to enhance this answer with such information if required but for the time being I am going to stick to a solution for your present problem. I am assuming python 3.
Define an additional class, thus:
class ModelSerializerMeta(serializers.SerializerMetaclass):
def __init__(cls, class_name, base_classes, attributes):
super(ModelSerialiserMeta, cls).__init__(class_name, base_classes, attributes)
Serializer.types[cls.Meta.oid] = [cls.Meta.model, cls]
Then use this as the metaclass of your Serializers, e.g.
class ProfileSerializer(serializers.ModelSerializer, metaclass=ModelSerializerMeta):
class Meta:
oid = 'profile'
model = Profile
fields = ['login', 'status']
Better yet, create some superclass for all your model serializers, assign the metaclass there, make all of your serializers inherit from that superclass which will then use the metaclass throughout.
Metaclasses are definitely the right answer unless your code can require python >= 3.6. Starting with 3.6 there is a new feature called the __init_subclass__ hook.
So you can do something like
class foo:
#classmethod
def __init_subclass__(cls, *args, **kwargs):
Serializers.register_class(cls)
Whenever a child of Foo is defined, the __init_subclass__ method on Foo will be called, passing in the child class reference as cls.
I am attempting to create a model class that all models will inherit from when they are created (in Django). I want any model class, with any attributes to be able to inherit from this class and read from the appropriate database table.
I know I am going need to use **kwargs and .setattr() at some point but am unclear as to where I even start. I am also going to try to recreate .all(), .filter() and .get() with in that class that all other methods that inherit this class can access.
This is what I have so far:
import sqlite3
class Model:
def __init__(self):
pass
#classmethod
def all(self, **kwargs):
pass
#classmethod
def get(self):
pass
#classmethod
def filter(self):
pass
###don't touch the code for these
class Users(Model):
pass
class Stocks(Model):
pass
How can I go about the initialization of this class?
It looks like you're trying to insert an abstract base class for your models.
Basically, what you've got there is correct, except you're missing
from django.db.models import Model
class MyModelBase(Model):
class Meta:
abstract = True
# ... The fields and methods I want all my models to inherit.
Then rather than making your models inherit from django.db.models.Model, they should inherit from MyModelBase.
In a model I usually put a "uuid" field for friendly URI, also a "slug" field.
Say I have a model named "SomeModel", by overriding its save() method, I can generate a uuid and a slug when it's being saved:
class SomeModel(models.Model):
...
def save(self, *args, **kwargs):
if not self.uuid:
uuid = shortuuid.uuid()[:10]
while SomeModel.objects.filter(uuid=uuid).exists():
uuid = shortuuid.uuid()[:10]
self.uuid = uuid
if not self.slug:
self.slug = slugify(self.title)[:500].rstrip('-')
super(SomeModel, self).save(*args, **kwargs)
It works well on regular model. Now I'd like to have an abstract model:
class SomeAbstractModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
...
And then:
class SomeModel(SomeAbstractModel):
class Meta(SomeAbstractModel.Meta):
...
The problem is, in the abstract model, looks like I cannot just simply replace
while SomeModel.objects.filter(uuid=uuid).exists():
with
while SomeAbstractModel.objects.filter(uuid=uuid).exists():
because abstract model doesn't have a manager.
I was wondering in this case, how can I avoid having redundant code in all models' save() methods. Also I'm not sure if
while SomeModel.objects.filter(uuid=uuid).exists():
is the best practice to check if an uuid exists or not.
Not sure if it is the prettiest way in town but this should work:
while self.__class__.objects.filter(...):
pass
When you create SomeModel(SomeAbstractModel), just create the class Meta from scratch without inheriting. By inheriting vom SomeAbstractModel.Meta you make it abstract again, and you cannot query on abstract model, not because they have no manager, but because there are no tables created.
So either you do this:
class SomeModel(SomeAbstractModel):
...
class Meta(SomeAbstractModel.Meta):
abstract=False
... your other model specific options
Or you do this (if you do not have any other model specific options:
class SomeModel(SomeAbstractModel):
...