I have two models A and B of the same structure derived from the same abstract model:
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
# ... # more fields
class Meta:
abstract = True
class A(CommonInfo):
pass
class B(CommonInfo):
pass
Now having an object of class A, I want to create an object of class B with the same values of fields.
What is the proper Django way to copy all fields of one object to the other?
The only way I know is to enumerate all fields (by the way, how to do it?) of an object and store them in the other object. But is there an easier way?
You could use model_to_dict(..) and use this dictionary in the construction of a B object, like:
from django.forms.models import model_to_dict
my_b = B(**model_to_dict(
my_a,
fields=[f.name for f in CommonInfo._meta.fields],
))
# some processing
my_b.save()
Note that if the CommonInfo contains foreign keys to objects, then these references will be copied, but no new referred objects will be constructed. Furthermore this will not work for many-to-many fields, so you need to exclude these (and add the related objects later).
Related
I have the following django model:
class Article(models.Model):
filename = models.CharField(max_length=255)
collection = models.ForeignKey(Collection, on_delete=models.CASCADE)
keys = ['filename', 'collection']
class Meta:
constraints = [
models.UniqueConstraint(
fields=['filename', 'collection'],
name='article_key')
]
As you can see I've defined the same list ['filename', 'collection'] in both the base class and the Meta class. I would like to define it once. I can't define it in Meta because then I get 'Meta got an unrecognised attribute 'keys'. So I must define it in the base class and access it from Meta. I don't know how to share data between the two. I've tried doing:
self.keys
in Meta but that gives 'self is not defined'. I've also tried with just 'keys' but that's also not defined. Any tips? Thanks.
EDIT
Thank you to Willem for pointing out that I can define keys in Meta if I just call it '_keys'. If I do this, however, the question is then how do I access _keys from the base class? I've tried 'meta._keys' and 'Meta._keys'. Both not defined.
EDIT 2
For clarity, the reason that I want 'keys' defined in the base class is that I will (a) be accessing it from properties on the base class, and (b) want to be able to access it from the outside.
You can declare it before the class, then reference it from both the model class and it' Meta:
# making it a tuple since you probably don't want
# it to be mutable
_ARTICLE_KEYS = ('filename', 'collection')
class Article(models.Model):
# making it an implementation attribute since you
# probably don't want to be writeable
# (hint: provide a read-only property for access)
_keys = _ARTICLE_KEYS
class Meta:
constraints = [
models.UniqueConstraint(
fields=_ARTICLE_KEYS,
name='article_key')
]
But this is still ugly IMHO and very probably unecessary - the model's methods should be able to access those values thru self._meta.contraints[0].fields or something similar (don't have models with such constraints at hand right now so I can check how this is actually transformed by the models's metaclass but inspecting self._meta in your django shell should give you the answer).
The methods of a nested class cannot directly access the instance attributes of the outer class.
So, in your case, If you won't use the keys list in the Article class, just defined it once in the Meta class. Otherwise, you need to defined twice!
Lets say i have 3 classes, A, B, C.
class A(models.Model):
comment = models.CharField(max_length=600, default="None")
rating = models.IntegerField(default=1, choices=CHOICES, name='rating')
date = models.CharField(max_length=50, default='nonee')
class B(models.Model):
Aname = models.ForeignKey('A', related_name='AB')
classC = models.ForeignKey('C', related_name='BC')
class C(models.Model)
#some info
def average_rating(self):
return self.?????.all().aggregate(Avg('rating')).values()[0]
How is it that I go from a view where my self is an object, all the way back to Class A so that I can aggregate the rating numbers. If i understand this correctly, the whole point of class B is just to be an object which shows relationships? I have been able to go between two classes, but when a third "relational" one is there i can't seem to get it to work.
When an operation needs to be performed on a recordset (queryset) basis rather than single record (model), then you should consider custom managers.
Adding extra Manager methods is the preferred way to add “table-level” functionality to your models. (For “row-level” functionality – i.e., functions that act on a single instance of a model object – use Model methods, not custom Manager methods.)
You don't need class B at all. What you need is a ManyToManyField between A and C; that will, behind the scenes, create a table similar to B, but unless you actually need to add fields on that table you're better off not defining it explicitly.
Once you've added the M2M on C, your average_rating method can use it directly:
class C(models.Model)
model_a_s = models.ManyToManyField('A')
def average_rating(self):
return self.model_a_s.all().aggregate(Avg('rating')).values()[0]
(Note, the title of your question is a bit confusing; there are no views involved here at all.)
So I want to create a django filters.FilterSet from django-filter module, but I want to dynamically add its attributes. For example, if I wanted to add SubName dynamically:
class UsersInfoFilter(filters.FilterSet):
Name=NumberFilter(lookup_type='gte')
def __new__(self):
self.SubName=NumberFilter(lookup_type='gte')
self.Meta.fields.append('SubName')
class Meta:
model = UsersInfo
fields = ['UserID', 'LanguageID', 'Name']
The problem is that FilterSet is a metaclass that immediately runs once the class has been figured out, so there is nowhere before that point that items can be dynamically added.
I've tried putting a function in as a parameter around filters.FilterSet class UsersInfo(AddObjects(filters.FilterSet)) which returns exactly what is passes, but I cannot reference UsersInfoFilter at that point since it still isn't finished being created.
I also tried making UsersInfoFilter its own base class, and then creating a class RealUsersInfoFilter(UsersInfoFilter, filters.FilterSet) as my actual filter, but then FilterSet just throws warnings about missing attributes named as fields.
There doesn't seem to be any kind of constructor function for classes in python. I'm assuming I have to do some kind of magic with metaclasses, but I've tried every combination I can think of and am at wits end.
You can't change Meta subclass from the __init__ method... there are 2 options to approach your issue...
First one - define "wide" filter on all of the model fields:
class UsersInfoFilter(filters.FilterSet):
class Meta:
model = UsersInfo
It will create default filters for all your model fields.
Second, define dynamic fields:
class UsersInfoFilter(filters.FilterSet):
name = NumberFilter(lookup_type='gte')
def __init__(self):
super(UsersInfoFilter, self).__init__()
base_filters['subname'] = NumberFilter(name='subname', lookup_type='gte')
class Meta:
model = UsersInfo
fields = ['user_id', 'language_id', 'name']
(I do not know if this is something you really want - because despite "dynamic" adding field - it should be declared as static - there are no logic here)
p.s.
why CamelCase on properties and fields? use proper pep-8.
To dynamically choose the fields in the FilterSet, I suggest to create a FilterSet factory like this:
def filterset_factory(model, fields):
meta = type(str('Meta'), (object,), {'model': model, 'fields': fields})
filterset = type(str('%sFilterSet' % model._meta.object_name),
(FilterSet,), {'Meta': meta})
return filterset
And then use it like:
DynamicFilterClass = filterset_factory(model=MyModel, fields=[...])
dynamic_filter = DynamicFilterClass(request.GET, queryset=instances)
I have a supplied database schema for which I want to create a Django application. Many of the tables in the schema share a common set of columns, such as name and date_created. That prompted me to create an abstract Standard_model class containing those columns, and subclass the relevant models from it.
Unfortunately, some of the tables have a name column with a different max_length. I'm trying to come up with a way for the subclassed model to pass the max_length value to the abstract base class, but I'm drawing a blank.
Any ideas?
class Standard_model(models.Model):
name = models.CharField(max_length=50)
date_created = models.DateTimeField()
class Meta:
abstract = True
class MyModel(Standard_model):
name = models.CharField(max_length=80) # Can't do this.
No, you cannot override the name field definition:
In normal Python class inheritance, it is permissible for a child
class to override any attribute from the parent class. In Django, this
is not permitted for attributes that are Field instances (at least,
not at the moment). If a base class has a field called author, you
cannot create another model field called author in any class that
inherits from that base class.
See also:
In Django - Model Inheritance - Does it allow you to override a parent model's attribute?
And, FYI, according to the model naming convention, it should be called StandardModel.
Suppose I have the following models:
class User(models.Model):
pass
class A(models.Model):
user = models.ForeignKey(User)
class B(models.Model):
a = models.ForeignKey(A)
That is, each user owns some objects of type A, and also some of type B. Now, I'm writing a generic interface that will allow the user to view any objects that it owns. In a view, of course I can't say something like "objects = model.objects.filter(user=user)", since B has no attribute 'user'. What's the best approach to take here?
The way I would do it is to simply go through the object 'a' on class B. So in the view, I would do:
objects = B.objects.get(user=a.user)
objects += A.objects.get(user=user)
The reason I would do it this way is because these are essentially two database queries, one to retrieve a bunch of object A's and one to retrieve a bunch of object B's. I'm not certain it's possible in Django to retrieve a list of both, simply because of the way database inheritance works.
You could use model inheritance as well. This would be making a base class for both objects A and B that contains the common fields and then retrieving a list of the base classes, then convert to their proper types.
Edit: In response to your comment, I suggest then making a base class that contains this line:
user = models.ForeignKey(User)
Class A and B can then inherit from that base class, and you can thus now just get all of the objects from that class. Say your base class was called 'C':
objects = C.objects.get(user=user)
That will obtain all of the C's, and you can then figure out their specific types by going through each object in objects and determining their type:
for object in objects:
if object.A:
#code
if object.B:
#code