I'd like to create a many-to-many relationship from and to a user class object.
I have something like this:
class MyUser(models.Model):
...
blocked_users = models.ManyToManyField(MyUser, blank=True, null=True)
The question is if I can use the class reference inside itself. Or do I have to use "self" insead of "MyUser" in the ManyToManyField? Or is there another (and better) way to do it?
Technically, I'm pretty sure "MyUser" or "self" will work, as long as it's a string in either case. You just can't pass MyUser, the actual class.
However, the docs always use "self". Using "self" is not only more explicit about what's actually happening, but it's impervious to class name changes. For example, if you later changed MyUser to SomethingElse, you would then need to update any reference to "MyUser" as well. The problem is that since it's a string, your IDE will not alert you to the error, so there's a greater chance of your missing it. Using "self" will work no matter what the class' name is now or in the future.
class MyUser(models.Model):
...
blocked_users = models.ManyToManyField("self", blank=True)
Don't forget use symmetrical=False, if you use .clear() or .add() method for related objects and don't wanna object on other side of relation update own data in relation field.
some_field = models.ManyToManyField('self', symmetrical=False)
I think it should be class name instead of self. because with using self like this
parent = models.ManyToManyField('self', null=True, blank=True)
when i add parent:
user1.parent.add(user2)
i have 2 record in database like this:
and with using class name liken this:
parent = models.ManyToManyField('User', null=True, blank=True)
i have one record in database like this:
note that i use uuid for pk and i use django 3.1
EDIT:
as #shinra-tensei explained as comment in this answer we have to set symmetrical to False if we use self. documented in Django Documents: ManyToManyField.symmetrical
If you use self or MyUser you will get a NameError in both cases. You should write "self" as string. See the example below:
class MyUser(models.Model):
...
blocked_users = models.ManyToManyField("self", blank=True, null=True)
And do not forget to set the symmetrical attribute to False if the relationship is not symmetrical.
For further details check: https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ManyToManyField
don't use 'self' in ManyToManyField, it will cause you object link each other, when use django form to submit it
class Tag(models.Model):
...
subTag = models.ManyToManyField("self", blank=True)
...
aTagForm.save()
and result:
a.subTag == b
b.subTag == a
Related
Recently I've faced an issue with Django, model inheritance and how the creation of model instances works.
Suppose I have the following (basic) setup:
class InviteBaseManager(models.Manager):
def create(self):
new_code = # create some kind of unique code, not really relevant here.
return super().create(code=new_code)
class InviteBase(models.Model):
code = models.CharField(max_length=10, blank=False, null=False, unique=True)
creationDate = models.DateTimeField(default=timezone.now())
objects = InviteBaseManager()
class PartyInviteManager(models.Manager):
def create(self, name):
# method 1
newInvite = InviteBase.objects.create()
print(newInvite.code) # is definitly set, lets assume "ABCD"
# as expexted, the "InviteBase" table has one row with code "ABCD" and
# default creationDate
newPartyInvite = super().create(partyName=name, invite=newInvite)
print(newPartyInvite.invite.code) # is EMPTY, prints nothing
# In fact, when looking at the db, there is still only *one* row in the table "InviteBase",
# with an *empty* code field and a default creationDate field.
return newPartyInvite
#method 2
newPartyInvite = super().create(partyName=name)
# creates the InviteBase instance implicitly, again, newPartyInvite.invite.code is empty.
# fill newPartyInvite.invity.code manually.
return newPartyInvite
class PartyInvite(InviteBase):
#Isn't blank=False and null=False unnecessary? A child should probably not exist with no parent?
invite = models.OneToOneField(InviteBase, parent_link=True, on_delete=models.CASCADE, null=False, blank=False)
partyName = models.CharField(...)
objects = PartyInviteManager()
So the question is: How can I pass an already existing instance of the base class inside the create method of my PartyInviteManager? Even when using method 1, the existing instance that I pass along seems to be overwritten. A new one is created with the default value. Interestingly enough, this violates the constraints that code cannot be blank or NULL.
This behaviour seems a bit odd to me? Can someone point out what I am missing here?
To clarify: I know that I should usually use **kwargs in the create methods and that inheritance might not be the ideal use case here, but I'm just very curious about this behaviour.
I know that this kind of model inheritance wont even create a pk for the child model (because holding a OneToOneField to the parent class effectively acts as a pk anyway), but why would it be impossible to pass a manually created instance as parent? Am I not allowed to use inheritance for my use-case?
I think I've found the correct way of doing this:
class InviteBaseManager(models.Manager):
def create(self, **kwargs):
kwargs['code'] = #createCode
return super().create(**kwargs)
class InviteBase(models.Model):
code = models.CharField(max_length=10, blank=False, null=False, unique=True)
creationDate = models.DateTimeField(default=timezone.now())
objects = InviteBaseManager()
class PartyInviteManager(InviteBaseManager):
def create(self, name):
return = super().create(partyName=name)
class PartyInvite(InviteBase):
invite = models.OneToOneField(InviteBase, parent_link=True, on_delete=models.CASCADE, null=False, blank=False)
partyName = models.CharField(...)
objects = PartyInviteManager()
The PartyInviteManager now inherits from the InviteManager as well. When calling super.create() from the child manager, the base manager gets called, appends the code field and everything works as expected.
I also found out, that if the PartyInviteManager does not inherit from the InviteBaseManager, the create method of InviteBaseManager does not get called when creating a new PartyInvite. This seems very odd to me.
Of course, the easier way would have been to create the code as default value via a function, like that:
def createCode():
return "ABCD" # add fancy code creation magic here.
class InviteBase:
code = models.CharField(max_length=128, blank=False, null=False, default=createCode)
But if, depending on the child class, the code needs additional information (such as invited members or whatever), this approach would not work anymore.
On a side note, interestingly enough the following code snippet:
invite = PartyInviteManager.objects.create(partyName='Birthday')
print(invite.invite.code)
print(invite.code)
produces the following output:
ABCD
ABCD
In the create method of the PartyInviteManager, one can directly use code=XXXX to pass along a string for the InviteBase model, invite_code on the other hand does not work.
please help solve the problem.
have an object:
class Product:
title = models.CharField(max_length=255)
active = models.BooleanField(default=False)
I create a new object:
class Book(Product):
slogan = models.CharField(max_length=255)
active = models.BooleanField(default=True) #???????
I need to override the attribute active. that is, to attribute always been active=True
at the same time I can not change the Product since I do not have access to it
You have two options. You can use save() method in your model or you can use pre_save() signal.
Unfortunately, that is not possible, see https://docs.djangoproject.com/en/dev/topics/db/models/#field-name-hiding-is-not-permitted
I have two model
class ClassProfile(models.Model):
classname = models.CharField(max_length=100, blank=True)
class UserProfile(models.Model):
user = models.OneToOneField(User)
class = models.ManyToManyField('ClassProfile')
I try to get all the famulymember like this
class = Class.objects.get(pk=1)
members = class.userprofile_set.all()
this will rasie an error
'ClassProfile' object has no attribute 'userprofile_set'
what's wrong with my code?
What makes you think django uses CamelCase anywhere? By default, the reverse accessor is lowercaseclsname_set
So class.userprofile_set.all() should do it.
Aside from the fact that it is not a good idea to name a variable class, I think you have a typo in this line:
class = Class.objects.get(pk=1)
You probably meant:
class = ClassProfile.objects.get(pk=1)
Google has lots of examples of doing reverse lookup in the interactive prompt but none of doing them inside a django model as a method.
I have the following models.py file:
class Client(models.Model):
...
def __unicode__(self):
return ???
class ClientDetails(models.Model):
client = models.ForeignKey(Client, null=True)
created = models.DateTimeField(default=datetime.now)
created_by = models.ForeignKey(User, null=True)
name_title = models.CharField(max_length=3, choices=NAME_TITLE_CHOICES)
first_name = models.CharField(max_length=40)
middle_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
...
How do I get the client method to return last_name from ClientDetails?
If a ClientDetails object should only ever be associated with a single Client object, then I would change your FK to a OneToOneField, which will provide you with a neat reverse accessor that can only ever link between a given Client and its associated ClientDetails. Then you can do:
try:
return self.clientdetails.last_name
except ClientDetails.DoesNotExist:
##handle it here, returning a graceful message
Alternatively, if you kept it as a FK, then you'd have to do something like:
try:
self.clientdetails_set.all()[0].last_name
except IndexError, e:
## handle exception here
but using a FK here is brittle and not great form (as the exception-handling implies: if there is none returned, then you'll get an IndexError. Also, there might be more than one ClientDetails object linked to that Client, and you'd only get the details of the first one here.)
So, I really would recommend using a OneToOneField instead of that FK. (All a OneToOneField is basically an FK with unique=True set on it and some neater accessors than standard FKs)
For that you can use in client the *clientdetails_set* to access all the ClientDetails objects that are linked to that client.
The set is an object set from Django, so calling the method all() will retrieve every of the objects. If you know there's only one you can do self.clientdetails_set.all()[0].last_name
Here's the link for the django documentation: Link
I'm looking to do this:
class Place(models.Model):
name = models.CharField(max_length=20)
rating = models.DecimalField()
class LongNamedRestaurant(Place): # Subclassing `Place`.
name = models.CharField(max_length=255) # Notice, I'm overriding `Place.name` to give it a longer length.
food_type = models.CharField(max_length=25)
This is the version I would like to use (although I'm open to any suggestion):
http://docs.djangoproject.com/en/dev/topics/db/models/#id7
Is this supported in Django? If not, is there a way to achieve similar results?
Updated answer: as people noted in comments, the original answer wasn't properly answering the question. Indeed, only the LongNamedRestaurant model was created in database, Place was not.
A solution is to create an abstract model representing a "Place", eg. AbstractPlace, and inherit from it:
class AbstractPlace(models.Model):
name = models.CharField(max_length=20)
rating = models.DecimalField()
class Meta:
abstract = True
class Place(AbstractPlace):
pass
class LongNamedRestaurant(AbstractPlace):
name = models.CharField(max_length=255)
food_type = models.CharField(max_length=25)
Please also read #Mark answer, he gives a great explanation why you can't change attributes inherited from a non-abstract class.
(Note this is only possible since Django 1.10: before Django 1.10, modifying an attribute inherited from an abstract class wasn't possible.)
Original answer
Since Django 1.10 it's
possible!
You just have to do what you asked for:
class Place(models.Model):
name = models.CharField(max_length=20)
rating = models.DecimalField()
class Meta:
abstract = True
class LongNamedRestaurant(Place): # Subclassing `Place`.
name = models.CharField(max_length=255) # Notice, I'm overriding `Place.name` to give it a longer length.
food_type = models.CharField(max_length=25)
No, it is not:
Field name “hiding” is not permitted
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.
That is not possible unless abstract, and here is why: LongNamedRestaurant is also a Place, not only as a class but also in the database. The place-table contains an entry for every pure Place and for every LongNamedRestaurant. LongNamedRestaurant just creates an extra table with the food_type and a reference to the place table.
If you do Place.objects.all(), you also get every place that is a LongNamedRestaurant, and it will be an instance of Place (without the food_type). So Place.name and LongNamedRestaurant.name share the same database column, and must therefore be of the same type.
I think this makes sense for normal models: every restaurant is a place, and should have at least everything that place has. Maybe this consistency is also why it was not possible for abstract models before 1.10, although it would not give database problems there. As #lampslave remarks, it was made possible in 1.10. I would personally recommend care: if Sub.x overrides Super.x, make sure Sub.x is a subclass of Super.x, otherwise Sub cannot be used in place of Super.
Workarounds: You can create a custom user model (AUTH_USER_MODEL) which involves quite a bit of code duplication if you only need to change the email field. Alternatively you can leave email as it is and make sure it's required in all forms. This doesn't guarantee database integrity if other applications use it, and doesn't work the other way around (if you want to make username not required).
See https://stackoverflow.com/a/6379556/15690:
class BaseMessage(models.Model):
is_public = models.BooleanField(default=False)
# some more fields...
class Meta:
abstract = True
class Message(BaseMessage):
# some fields...
Message._meta.get_field('is_public').default = True
My solution is as simple as next monkey patching, notice how I changed max_length attribute of name field in LongNamedRestaurant model:
class Place(models.Model):
name = models.CharField(max_length=20)
class LongNamedRestaurant(Place):
food_type = models.CharField(max_length=25)
Place._meta.get_field('name').max_length = 255
Pasted your code into a fresh app, added app to INSTALLED_APPS and ran syncdb:
django.core.exceptions.FieldError: Local field 'name' in class 'LongNamedRestaurant' clashes with field of similar name from base class 'Place'
Looks like Django does not support that.
This supercool piece of code allows you to 'override' fields in abstract parent classes.
def AbstractClassWithoutFieldsNamed(cls, *excl):
"""
Removes unwanted fields from abstract base classes.
Usage::
>>> from oscar.apps.address.abstract_models import AbstractBillingAddress
>>> from koe.meta import AbstractClassWithoutFieldsNamed as without
>>> class BillingAddress(without(AbstractBillingAddress, 'phone_number')):
... pass
"""
if cls._meta.abstract:
remove_fields = [f for f in cls._meta.local_fields if f.name in excl]
for f in remove_fields:
cls._meta.local_fields.remove(f)
return cls
else:
raise Exception("Not an abstract model")
When the fields have been removed from the abstract parent class you are free to redefine them as you need.
This is not my own work. Original code from here: https://gist.github.com/specialunderwear/9d917ddacf3547b646ba
Maybe you could deal with contribute_to_class :
class LongNamedRestaurant(Place):
food_type = models.CharField(max_length=25)
def __init__(self, *args, **kwargs):
super(LongNamedRestaurant, self).__init__(*args, **kwargs)
name = models.CharField(max_length=255)
name.contribute_to_class(self, 'name')
Syncdb works fine. I dont tried this example, in my case I just override a constraint parameter so ... wait & see !
I know it's an old question, but i had a similar problem and found a workaround:
I had the following classes:
class CommonInfo(models.Model):
image = models.ImageField(blank=True, null=True, default="")
class Meta:
abstract = True
class Year(CommonInfo):
year = models.IntegerField()
But I wanted Year's inherited image-field to be required while keeping the image field of the superclass nullable. In the end I used ModelForms to enforce the image at the validation stage:
class YearForm(ModelForm):
class Meta:
model = Year
def clean(self):
if not self.cleaned_data['image'] or len(self.cleaned_data['image'])==0:
raise ValidationError("Please provide an image.")
return self.cleaned_data
admin.py:
class YearAdmin(admin.ModelAdmin):
form = YearForm
It seems this is only applicable for some situations (certainly where you need to enforce stricter rules on the subclass field).
Alternatively you can use the clean_<fieldname>() method instead of clean(), e.g. if a field town would be required to be filled in:
def clean_town(self):
town = self.cleaned_data["town"]
if not town or len(town) == 0:
raise forms.ValidationError("Please enter a town")
return town
You can not override Model fields, but its easily achieved by overriding/specifying clean() method. I had the issue with email field and wanted to make it unique on Model level and did it like this:
def clean(self):
"""
Make sure that email field is unique
"""
if MyUser.objects.filter(email=self.email):
raise ValidationError({'email': _('This email is already in use')})
The error message is then captured by Form field with name "email"