how to access may-to-many relationship in django? - python

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)

Related

DRF serializers how to best handle indirect references to a FK?

Let's say I have the following models:
class Thing(models.Model):
thing_key = models.AutoField(primary_key=True)
state = models.ForeignKey(State, db_column='state_key', on_delete=models.CASCADE, default=0)
[...other fields unrelated to the question...]
class Meta:
db_table = 'THING'
class State(models.Model):
state_key = models.AutoField(primary_key=True)
flag = models.IntegerField(unique=True)
description = models.CharField(max_length=50)
class Meta:
db_table = 'STATE'
And let's say I need to create new Things based on data received via POST requests.
And let's say these POST requests do NOT contain the FK of Thing, which is state_key, but actually the flag field from the State model.
What is the best way to implement serializers that help accomplish the following things:
Create a new Thing even though the state_key is unknown.
Return a serialized representation of the newly created Thing without exposing the state_key.
After reading and re-reading the documentation, the best I could do is the following. It works, but I'm suspect there's a much more straightforward way to do it:
class FlagField(serializers.RelatedField):
def to_representation(self, value):
return value.flag
def to_internal_value(self, data):
return State.objects.get(flag=data)
class ThingSerializer(serializers.ModelSerializer):
state_flag = FlagField(queryset=State.objects.all(), source='state')
class Meta:
model = Thing
exclude = ['state']
Is this an acceptable approach? If not, what is wrong with it and how else could I accomplish the goal?
Thanks in advance!

Cannot use a string in Django ManyToManyField through value

(Apologies if I'm missing some critical info to help diagnose this -- I'm new to Python and Django.)
Django complains when I try to use a string in my ManyToManyField through attribute:
File "/vagrant/flamingo_env/lib/python3.4/site-packages/django/db/models/fields/related.py", line 1366, in _check_relationship_model
for f in through._meta.fields:
AttributeError: 'str' object has no attribute '_meta'
Usage:
class Voter(models.Model):
# ...
addresses = models.ManyToManyField(Address, through='voter_addresses', through_fields=('voter_id', 'address_id'))
The error goes away if I create a Through model:
class VoterAddress(models.Model):
voter_id = models.ForeignKey(Voter)
address_id = models.ForeignKey(Address)
class Meta:
db_table = 'voter_addresses'
But of course then it complains that Voter hasn't been defined--and I can't simply change the order, or else VoterAddress won't have been defined either.
And in every single example I have seen the basic string version used. What's going on?
You need to fix the name passed to your through
addresses = models.ManyToManyField(Address, through='VoterAddress')
It has to be the exact name of the custom throughmodel
You will not be required to create a throughmodel if you do not pass the through argument. Django will create and manage one for you
To fix your ordering issue you can give a string value to the ForeignKey class.
class VoterAddress(models.Model):
voter_id = models.ForeignKey("Voter")
address_id = models.ForeignKey("Address")
class Meta:
db_table = 'voter_addresses'
This is a way to define foreignkeys to Models that haven't been defined in the file yet.
If you change this Django won't complain that Voter hasn't been defined.

Django get attributes from foreign key's class

I would like to show one attribute from another class. The current class has a foreign key to class where I want to get the attribute.
# models.py
class Course(models.Model):
name = models.CharField(max_length=100)
degree = models.CharField(max_length=15)
university = models.ForeignKey(University)
def __unicode__(self):
return self.name
class Module(models.Model):
code = models.CharField(max_length=10)
course = models.ForeignKey(Course)
def __unicode__(self):
return self.code
def getdegree(self):
return Course.objects.filter(degree=self)
# admin.py.
class ModuleAdmin(admin.ModelAdmin):
list_display = ('code','course','getdegree')
search_fields = ['name','code']
admin.site.register(Module,ModuleAdmin)
So what i'm trying to do is to get the "degree" that a module has using the "getdegree". I read several topics here and also tried the django documentation but i'm not an experienced user so even I guess it's something simple, I can't figure it out. Thanks!
It is pretty straight forward.
Try this:
def getdegree(self):
return self.course.degree
Documentation here
You can do this safely because course is not a nullable field. If it were, you should have checked for existence of object before accessing its attribute.

Django Many-to-Many (m2m) Relation to same model

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

In Django - Model Inheritance - Does it allow you to override a parent model's attribute?

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"

Categories