This code is a abstract class for many
Class Base (models.Model):
Created_by = models.ForeignKey(User)
Modified_by = models.ForeignKey(User, null=True)
I want related_name like this related_name = self.default_related_name + '_name_field'
As the following
Class Base(models.Model):
Created_by = models.ForeignKey(User,
related_name = self.default_related_name + '_created_by')
Modified_by = models.ForeignKey(User,
null = True,
related_name = self.default_related_name + '_modified_by')
But i know that, I cant have access to instance in the attributes of the class.
So what method do I can to overload to create a field with a method (or property)?
(I tried to create the field in the __init__ method, but it doesnt not work).
If you can use the subclass name instead of default_related_name then it's trivial:
related_name="%(class)s_created"
https://docs.djangoproject.com/en/1.8/topics/db/models/#be-careful-with-related-name
Also, as the base class is a abstract model, you will need
class Meta:
abstract = True
Pre save signal is what you are looking for.
for reference : https://docs.djangoproject.com/en/1.11/ref/signals/#pre-save
right before save it to the database, you can apply all the changes you want. :)
here is an example where I capitalize the first letter of every word and create a slug.
def pre_save_city(sender, instance, *args, **kwargs):
instance.name = instance.name.title()
if not instance.slug:
instance.slug = slugify(instance.name)
this is how you run the function :
pre_save.connect(pre_save_city,sender=City)
Related
I'm using Django 1.10
In our base model (that few model inherit from) we set
class BaseModel(models.Model):
created_at = models.DateTimeField(db_index=True, auto_now_add=True)
now, in specific sub-class model I need to override it's save and update the 'created_at':
class Item(BaseModel):
title = models.CharField(max_length=200)
identifier = models.CharField(max_length=15)
def save(self, *args, **kwargs):
existing_item = Item.objects.active_and_deleted().get(
identifier=self.identifier)
existing_item.created_at = now()
super(Item, existing_item).save(args, kwargs)
That updated instance created_at is 'None'.
I've tried 'editable=True' unsuccessfully.
Any idea?
With the following example, change the default parameter of DateTimeField, django will allow you to edit it manually: assign the attribute with django.utils.timezone.now() in the save() method
import django
class BaseModel(models.Model):
created_at = models.DateTimeField(db_index=True,
default=django.utils.timezone.now())
Is there a way to Subclass a Django ManyRelatedManager a.k.a. ManyToManyField ?
The goal is to pre-filter all related models when calling the ManyRelatedManager by a flag of deleted=None. If deleted=None then it is a valid Model.
So far, this is the code, but it doesn't seem to work.
class ExcludeDeletedManyToManyField(models.ManyToManyField):
def get_queryset(self):
qs = super(ExcludeDeletedManyToManyField, self).get_queryset()
return qs.filter(deleted__isnull=True)
class SelfRefrencingModel(models.Model):
children = ExcludeDeletedManyToManyField('self', blank=True,
symmetrical=False, related_name='parents')
You can create proxy model of SelfRefrencingModel and override the default manager. Then use this proxy model in ManyToManyField.
Subclassing ManyToManyField will not help you because for the resulting queryset ManyRelatedManger is responsible.
Proxy model approach:
from django.db import models
class A(models.Model):
children = models.ManyToManyField('AProxy')
name = models.TextField()
deleted = models.NullBooleanField(null=True)
class FilterDeletedManager(models.Manager):
def get_queryset(self):
qs = super(FilterDeletedManager, self).get_query_set()
return qs.filter(deleted__isnull=True)
class AProxy(A):
objects = FilterDeletedManager()
class Meta:
proxy = True
Caveat with this approach is that now django expects AProxy instances for children field.
So maybe better readable and maintainable approach will be to add another attribute in __init__.
from django.db import models
class A(models.Model):
children = models.ManyToManyField('self')
name = models.TextField()
deleted = models.NullBooleanField(null=True)
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
self.deleted_null_children = self.children.filter(deleted__isnull=True)
Here is my solution. #beezz, you may be correct to use a Proxy Model to do this, but I haven't used a Proxy Model before for this pattern, so this is how I solved this:
class SelfRefrencingQuerySet(models.query.QuerySet):
pass
class SelfRefrencingManager(BaseManager):
def get_queryset(self):
return SelfRefrencingQuerySet(self.model, self._db).filter(
deleted__isnull=True)
class SelfRefrencingBaseModel(models.Model):
children = models.ManyToManyField('self', blank=True, symmetrical=False,
related_name='parents')
# Manager
objects = SelfRefrencingManager()
objects_all = models.Manager() # So you still have acccess to the
# default Manager
If your intention is to use this with Django Admin or ModelForm; you don't need to subclass the ManyToManyField. See the django documentation
class SelfRefrencingModel(models.Model):
children = models.ManyToManyField('self', blank=True, symmetrical=False,
related_name='parents', limit_choices_to={'deleted': False}))
Note: If deleted is a BooleanField it has to be True or False. It can't be None/NULL.
beezz's idea of using a Proxy Model is also a good one.
What I sometimes do is to customise the default manager
class MyModelManager(models.Manager):
use_for_related_fields = True
def get_queryset(self):
qs = super(MyModelManager, self).get_queryset()
return qs.filter(deleted=False)
class MyModelManager(models.Model):
objects = MyModelManager()
_objects = models.Manger()
deleted = models.BooleanField(default=False)
By default deleted objects will be hidden, but if need them in your queryset, you can use _objects.
I am trying to create the following models. There is a ManyToMany relation from Entry to AUTH_USER_MODEL via the EntryLike intermediate model.
class BaseType(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
creation_time = models.DateTimeField(auto_now_add=True)
last_update_time = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Title(BaseType):
text = models.CharField(max_length=100)
description = models.TextField()
class EntryLike(BaseType):
entry = models.ForeignKey(Entry)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
class Entry(BaseType):
title = models.ForeignKey(Title, on_delete=models.PROTECT)
text = models.TextField()
user = models.ForeignKey(settings.AUTH_USER_MODEL)
liked_by_users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='EntryLike', through_fields=('entry', 'user'))
Running migrations on the above model scheme throws the error: AttributeError:'str' object has no attribute 'meta'.
Any help in resolving this error would be highly appreciated. Am new to Django & Python, but not to Web Development.
The issue is that settings.AUTH_USER_MODEL is almost certainly not a model instance. It's probably a string that constrains the choices another model can make - settings would be a strange place to leave a model definition.
To do a MTM between the user model and your field above you need need to do:
from django.contrib.auth.models import User
class Entry(BaseType):
title = models.ForeignKey(Title, on_delete=models.PROTECT)
text = models.TextField()
user = models.ForeignKey(User)
def __str__(self):
return self.title
I've added the str function so that it gives a more sensible return when you're manipulating it in admin/shell.
I'd also question whether you need the second set of fields (removed here), as you can use select related between the Entry and EntryLike join table, without any duplication of the fields - you can probably go that way, it's just a bit unnecessary.
Lastly, I'd note that the way I'm using it above just uses the default User object that comes with Django - you may wish to customise it. or extend the base class as you've done here with your own models' base class.
(All of this is predicated on AUTH_USER_MODEL not being a model instance - if it is, can you post the model definition from settings.py? )
When I syncdb, I get many errors like this:
transcription.transcription1: Accessor for field 'participant_content_type' clashes with related field 'ContentType.auxi
liary_model_as_participant'. Add a related_name argument to the definition for 'participant_content_type'.
transcription.transcription1: Reverse query name for field 'participant_content_type' clashes with related field 'Conten
tType.auxiliary_model_as_participant'. Add a related_name argument to the definition for 'participant_content_type'.
My models already have related names:
# my base class which I intend to inherit from in many places.
# Many types of AuxiliaryModel will point at participant/match objects.:
class AuxiliaryModel(models.Model):
participant_content_type = models.ForeignKey(ContentType,
editable=False,
related_name = 'auxiliary_model_as_participant')
participant_object_id = models.PositiveIntegerField(editable=False)
participant = generic.GenericForeignKey('participant_content_type',
'participant_object_id',
)
match_content_type = models.ForeignKey(ContentType,
editable=False,
related_name = 'auxiliary_model_as_match')
match_object_id = models.PositiveIntegerField(editable=False)
match = generic.GenericForeignKey('match_content_type',
'match_object_id',
)
class Meta:
abstract = True
class Transcription(AuxiliaryModel):
transcription = models.TextField(max_length=TRANSCRIPTION_MAX_LENGTH,
null=True)
class Meta:
abstract = True
class Transcription1(Transcription):
pass
class Transcription2(Transcription):
pass
class Transcription3(Transcription):
pass
The problem goes away when I comment out Transcription2 and Transcription3, so it seems like the related_names clash. Do I have to make them unique? If so, is there a way to do this without having to write boilerplate code in each child class?
From the Django docs https://docs.djangoproject.com/en/dev/topics/db/models/#be-careful-with-related-name :
If you are using the related_name attribute on a ForeignKey or ManyToManyField, you must always specify a unique reverse name for the field. This would normally cause a problem in abstract base classes, since the fields on this class are included into each of the child classes, with exactly the same values for the attributes (including related_name) each time.
To work around this problem, when you are using related_name in an abstract base class (only), part of the name should contain '%(app_label)s' and '%(class)s'.
In this case, I think this will work:
participant_content_type = models.ForeignKey(ContentType,
editable=False,
related_name = '%(app_label)s_%(class)s_as_participant')
match_content_type = models.ForeignKey(ContentType,
editable=False,
related_name = '%(app_label)s_%(class)s_model_as_match')
So, using %(app_label)_transcription2_as_participant you can access the reverse of Transcription2.participant_content_type
I want to define two model fields: created_by, modified_by in a parent model, they will be acting as common fields for the child models.
class ExtendedModel(models.Model):
created_by = models.ForeignKey(User,related_name='r_created_by')
modified_by = models.ForeignKey(User,related_name='r_modified_by')
class Meta:
abstract = True
class ChildModel1(ExtendedModel):
pass
class ChildModel2(ExtendedModel):
pass
this gives errors as ChildModel1 and ChildModel2 has related_name clashed with each other on their created_by and modified_by fields.
The Django docs explain how to work around this: http://docs.djangoproject.com/en/dev/topics/db/models/#abstract-related-name
class ExtendedModel(models.Model):
created_by = models.ForeignKey(User,related_name='"%(app_label)s_%(class)s_created_by')
modified_by = models.ForeignKey(User,related_name='"%(app_label)s_%(class)s_modified_by')
class Meta:
abstract = True
class ChildModel1(ExtendedModel):
pass
class ChildModel2(ExtendedModel):
pass