I have the following model in django:
class Node(models.Model):
name = models.CharField(max_length=255)
And this subclass of the above model:
class Thingy(Node):
name = models.CharField(max_length=100)
otherstuff = models.CharField(max_length=255)
The problem with this setup is that while everything Just Works, a look into the database shows that syncdb has created two tables. One called appname_node with a column called name and another one called appname_thingy with two columns: name and otherstuff. When a new object is created, the name value is copied into both tables... not really cool if you dig the whole concept of normalisation :-)
Can someone explain to me how I might modify the max_length value of the "name" property in "Thingy" without re-defining it?
Your implementation is totally wrong, that is not how you suppose to write parent and child class. either define name in parent class or child class, if you define it in a parent class then you can't define again in the child because new field will be created instead. so if you want to change max_length in the child, then I would recommend that you declare name in the child so that any class that inherits from node will set its own name with its own max_length.
the correct implementation is
class Node(models.Model):
#other class attributes
child class should inherit parent attributes and add its own stuff.
class Thingy(Node):
name = models.CharField(max_length=100)
otherstuff = models.CharField(max_length=255)
now when you query, you only get one name instead of the two.
Related
If we create a new model as follows,
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
and run the database migrations, then I understand that
the corresponding table is created with column names and field types, and I understand that we can do the following:
>>> from myapp.models import Book
>>> book = Book(title="Matilda", author="Roald Dahl")
>>> book.save()
>>> print book.title
Matilda
However I am struggling a little to understand the python code
in the class definition, just in terms of how I understood classes to work, and was hoping someone could help
my understanding with the following questions:
In the definition of the class Book, title seems to be a
class attribute. This class attribute is set to an instance of
the CharField class? In which case when we create an instance of
the Book class, if we do not pass in an argument, would title
be set to this same instance of CharField?
When we pass in title='Matilda' on creating this instance of Book, is this then overriding the attribute title?
How do we know that Book takes in title as an argument?
I hope these make sense. Thank you for any help.
Lizzie
When you make a model class in Django, consider that class the data-table, each individual instance of that class the table rows, and the attributes(e.g: title) of each table the columns.
In the definition of the class Book, title seems to be a class attribute. This class attribute is set to an instance of the CharField class? In which case when we create an instance of the Book class, if we do not pass in an argument, would title be set to this same instance of CharField?
‘title’ would be a field in the data table and the ‘CharField’ would be a type of field; each field is assigned a type by you using classes like ‘CharField’, ‘TexrField’, etc. You don’t necessarily have to pass in a value for ‘title’ (or any field) if you give a default value when describing the type, example:
title = models.CharField(max_length=50, default=‘unknown’)
When we pass in title='Matilda' on creating this instance of Book, is this then overriding the attribute title?
You are giving it a value of ‘Matilda’ as it doesn’t have a title until you give it one (or provide a default when making the field).
How do we know that Book takes in title as an argument?
Because you wrote it in the model class; it really depends on how you’re going to input the data really. If you’re making some kind of page that takes input from a user, display the ‘title’ field in a form and make it mandatory. You can make fields optional when you assign the type of field, like
title = models.CharField(max_length=200, null=true, blank=true)
is there a way to get, from a list of ForeignKey-Parents, the one which got the newest Child?
For example:
class parent(models.Model):
name = models.CharField(*)
class child(models.Model):
parent = models.ForeignKey(parent, *)
Now, given a list of parents, i want to get the parent, which had a child added last. Is there an easy way to do that in Django?
Thanks for the Answers!
You can not rely on the primary key to know what was last added. You better add a timestamp for this. So you might want to change your model to:
class Parent(models.Model):
name = models.CharField(max_length=128)
class child(models.Model):
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
Then you can obtain the parent with the most recent child with:
Parent.objects.filter(pk__in=list_of_parents).order_by('-child__created').first()
This will return a Parent object if such parent exists, and None otherwise.
If adding a timestamp is not an option, you can try to work with the primary key, but a database does not per se distributes primary key values in a certain order:
# does not per se works
Parent.objects.filter(pk__in=list_of_parents).order_by('-child__pk').first()
I'm upgrading a project from django 1.8 to 1.10 and it looks like django has improved the check of eventual name collision between foreign keys and model inheritance.
This is obviously a good thing, but the projet I need to upgrade is a big one and it would be a hell to rename a model.
Let me explain the problem : I have a base class called Parent and many children which are linked together, like so :
class Parent(models.Model):
title = models.CharField(max_length=10)
class ChildA(Parent):
description = models.TextField()
class ChildB(Parent):
description = models.TextField()
childa = models.ForeignKey(ChildA)
The clash here is that a childb object has 2 "childa" attributes :
The "childa" ForeignKey
The instance inherited by the ChildA model (because childb has also the parent attributes).
The 2 obvious solutions here are :
Rename the ForeignKey ChildB.childa to ChildB.somethingelse
Rename the ChildA model to something else.
Both solutions costs a lot and will probably introduce new bugs.
So I wondered : Is it possible to rename the reverse related name of the inherited object ?
For example :
p = Parent.objects.get(pk=1)
print p.childa_child # Hit the ChildA instance
I have no idea if I'm clear enough but I'll keep this question up to date.
==== EDIT ====
To be more concise, if I have 2 models class Parent(models.Model) and class Child(Parent), a dynamic attribute parent.child is created.
Is it possible to edit this attribute name without touching the class name ?
Multi-table inheritance creates an implicit OneToOneField field between the base model and the subclass.
Django allows you to modify this relationship by explicitly setting the one to one field.
class Parent(models.Model):
title = models.CharField(max_length=10)
class ChildA(Parent):
parent = models.OneToOneField(to=Parent, parent_link=True)
description = models.TextField()
class ChildB(Parent):
parent = models.OneToOneField(to=Parent, parent_link=True)
description = models.TextField()
childa = models.ForeignKey(ChildA)
The important bit here is the parent_link=True argument which tells Django to use this field declaration for managing the multi-table inheritance with these two models.
So you can now set related_name='+' to prevent Django from creating a reverse relationship or you can set related_name to a more unique name:
class ChildA(Parent):
parent = models.OneToOneField(to=Parent, parent_link=True, related_name='child_a_here')
description = models.TextField()
class ChildB(Parent):
parent = models.OneToOneField(to=Parent, parent_link=True, related_name='child_b_here')
description = models.TextField()
childa = models.ForeignKey(ChildA)
I'm a little confused as to how ChildB has to two ChildA links, it looks like you left out some relationship fields from the models perhaps? Regardless, what I think you are looking for is the related_name parameter.
So with:
class ChildB(Parent):
description = models.TextField()
childa = models.ForeignKey(ChildA, related_name='b_children')
You could do a query like so:
a = ChildA.objects.get(id=1)
print(a.b_children)
You may also be interested in abstract models.
I would like to use properties from an inheriting model's Meta class to configure a field defined in an abstract model higher up the inheritance tree:
class NamedModel(models.Model):
class Meta:
abstract = True
verbose_name = 'object'
name = models.CharField("Name",
max_length=200,
db_index=True,
help_text="A meaningful name for this %s." % Meta.verbose_name)
# see what I'm trying to do here?
)
...
class OwnedModel(NamedModel):
class Meta(NamedModel.Meta):
verbose_name = 'owned object'
I would like the help text on the name field of OwnedModel forms to say 'A meaningful name for this owned object'. But it does not: the word 'owned' is missing, which would suggest that the verbose_name from the NamedModel.Meta is used when the model is set up, not OwnedModel.Meta.
This isn't quite what I expect from an inheritance point of view: is there some way to get the field to be created whereby Meta.verbose_name refers to the value on the non-abstract model class, not the abstract one on which the field was defined?
Or am I being daft?
(This may seem like a trivial example, and it is: but it's just to illustrate the point of something more important and complex I am trying to do)
Many thanks in advance.
Why don't you try to make a class.
class BaseNamedModelMeta:
abstract = True
verbose_name = "your text"
And then inherit and override whatever you want like this:
class OwnedModel(NamedModel):
class Meta(BaseNamedModelMeta):
verbose_name = 'owned object'
I think this happens because Meta.verbose_name is used and NamedModel.name is created when class NamedModel is parsed. So later, when class OwnedModel gets parsed, there is no chance to change anything.
Maybe you can set the help_text property on OwnedModel.name later on, but this may change NamedModel.name also.
In similar situations I have put the variable parts in class attribute of the model (not Meta) and then used the by run time methods/properties to generate the texts I need.
In fact I ended up doing the following. The base model gets given a dynamic_field_definition() class method, which can be used to patch up the fields, with the cls argument being the correct (inheriting) class. That means that that cls' Meta attributes are of that correct child, not the original base.
I then wire up that method to get called on the class_prepared signal, so that you know everything's otherwise ready.
class NamedModel(models.Model):
...
#classmethod
def dynamic_field_definition(cls):
pass
def dynamic_field_definition(sender, **kwargs):
if issubclass(sender, NamedModel):
sender.dynamic_field_definition()
class_prepared.connect(dynamic_field_definition)
Then the field properties that vary with model class are simply reconfigured by that class method (or more likely the method as overridden in derived classes).
It's a slightly hacky way to bring a last little bit of OO-ness to Django models, but it works fine for my purpose.
First of all,I'm not into web programming. I bumped into django and read a bit about models. I was intrigued by the following code ( from djangoproject.com ) :
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def __str__(self):
# Note use of django.utils.encoding.smart_str() here because
# first_name and last_name will be unicode strings.
return smart_str('%s %s' % (self.first_name, self.last_name))
By my understanding of python , first_name and last_name are class variables , right ? How is that used in code ( because I guess that setting Person.first_name or Person.last_name will affect all Person instances ) ? Why is it used that way ?
The essence of your question is "how come these class variables (which I assign Field objects to) suddenly become instance variables (which I assign data to) in Django's ORM"? The answer to that is the magic of Python metaclasses.
A metaclass allows you to hook into and modify the process of creating a Python class (not the creation of an instance of that class, the creation of the class itself).
Django's Model object (and thus also your models, which are subclasses) has a ModelBase metaclass. It looks through all the class attributes of your model, and any that are instances of a Field subclass it moves into a fields list. That list is assigned as an attribute of the _meta object, which is a class attribute of the model. Thus you can always get to the actual Field objects via MyModel._meta.fields, or MyModel._meta.get_field('field_name').
The Model.__init__ method is then able to use the _meta.fields list to determine what instance attributes should be initialized when a model instance is created.
Don't be afraid to dive into the Django source code; it's a great source of education!
Yes, first_name and last_name are class variables. They define fields that will be created in a database table. There is a Person table that has first_name and last_name columns, so it makes sense for them to be at Class level at this point.
For more on models, see:
http://docs.djangoproject.com/en/dev/topics/db/models/
When it comes to accessing instances of a Person in code, you are typically doing this via Django's ORM, and at this point they essentially behave as instance variables.
For more on model instances, see:
http://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs
Not a real answer, but for enrichment:
Person.first_name
won't work
p = Person.objects.get(pk=x)
p.first_name
will work. so an object instance of person has a first and last name, but static context Person does not.
Also note: Django has Model Managers which are allow "Person" to do static queryset operations. (https://docs.djangoproject.com/en/dev/topics/db/managers/#managers).
so for example
peoples = Person.objects.all()