I'm working in Django 2.0
I have a model Note to save note and two another models to add color labels to the note.
class Note(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=250, blank=True, default='Untitled')
content = models.TextField(blank=True)
class ColorLabels(models.Model):
title = models.CharField(max_length=100, unique=True)
value = models.CharField(max_length=100)
default = models.BooleanField(default=False)
class NoteLabel(models.Model):
note = models.OneToOneField(Note, on_delete=models.CASCADE)
color_label = models.OneToOneField(ColorLabels, on_delete=models.CASCADE)
with the object of Note
note = Note.objects.get(pk=1)
I want to access associated ColorLabels's title and value fields or the NoteLabel object.
since they are one to one field. I tried doing
note.note_label
note.NoteLabel
note.note_label_set
But all returns error as
AttributeError: 'Note' object has no attribute 'note_label_set'
Unless you define related_name in your OneToOneField, Django will use lowercased model name to access related object. So, note.notelabel should work.
Related
In my app I have the following models:
class Category(BaseStampModel):
cat_id = models.AutoField(primary_key=True, verbose_name='Cat Id')
category = models.CharField(max_length=55, verbose_name='Category')
class MasterList(BaseStampModel):
master_list_id = models.AutoField(primary_key=True, verbose_name='Master List Id')
mast_list_category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, verbose_name='Category')
# Other fields ...
My BaseModel looks like this:
class BaseStampModel(models.Model):
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='%(class)s_created', blank=True, null=True, on_delete=models.SET_NULL, verbose_name='Created by')
created_on = models.DateTimeField(auto_now_add = True, null=True, blank=True)
With this I am able to display the model objects and create/update instances.
In my view, when I want to retrieve the verbose_name from model "Category" using:
`model_fields = [(f.verbose_name, f.name) for f in Category._meta.get_fields()]`
I am getting the following error in my browser:
AttributeError: 'ManyToOneRel' object has no attribute 'verbose_name'
If I remove the the FK relationship from the field mast_list_category (make it a simple CharField) I don't get the error.
Gone through millions of pages, but no solution yet.
Any help is much appreciated.
You can't access the verbose_name just like accessing model fields
But there are different ways of getting verbose_name. The better way is using get_field
model._meta.get_field('field_name').verbose_name
In your case, replace the model with Category and field_name with the field you want to get the verbose_name
This is what I am doing:
Declare an empty array, loop through the model, get the field name and its verbose_name and append.
array_model_with_ver_name = []
for f in Category._meta.get_fields():
if hasattr(f, 'verbose_name'):
array_model_with_ver_name.append((f.verbose_name, f.name))
I have got a basic foreign key table structure
class Category(models.Model):
id_pk = models.AutoField(primary_key=True)
label = models.CharField(unique=True, max_length=200, blank=True, null=True)
class Meta:
managed = False
db_table = 'category'
and
class PartProperty(models.Model):
id_pk = models.AutoField(primary_key=True)
category = models.ForeignKey(Category, models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'part_property'
If a row inside PartProperty has a Category object I can access this object via e.g. part_property = PartProperty.objects.select_related().filter(id_pk=part_property_id) and
part_property[0].catogory respectively dynamically via part_property .values(*(self.__COLUMN_NAMES))[0]['self.__COLUMN_NAMES[i]] and set a new value and save the table where the foreign key points to.
But if part_property[0].catogory is None I have no object type for creating an object of the foreign key table.
Is it possible to get the object type dynamically from the PartProperty / PartProperty model?
You can get the model class for a field dynamically through the _meta property. e.g. PartProperty._meta.get_field("category").related_model will return the Category model class.
You can also get the list of fields of a model dynamically. See this link for the official documentation on the _meta API: https://docs.djangoproject.com/en/3.2/ref/models/meta/
I'm working with django, during inserting data into tables the error is generates as given below...
Error:
int() argument must be a string, a bytes-like object or a number, not 'Tbl_rule_category', How can we solve such error?
view.py
dataToRuleCtgry = Tbl_rule_category(category=category, created_by="XYZ",created_date=datetime.date.today())
dataToRuleCtgry.save()
dataToRule = Tbl_rule(rule_name=rule_name, closure=closure,category_id=Tbl_rule_category.objects.latest('category_id'), created_by="XYZ",created_date=datetime.date.today(), updated_by="XYZ", updated_date=datetime.date.today(), rule_type=rule_type, fk_tbl_rule_tbl_rule_category_id=Tbl_rule_category.objects.latest('category_id'))
dataToRule.save()
models.py
class Tbl_rule_category(models.Model):
category_id = models.AutoField(primary_key=True)
category = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
def __str__(self):
pass # return self.category, self.created_by
class Tbl_rule(models.Model):
rule_id = models.AutoField(primary_key=True)
rule_name = models.CharField(max_length=50)
closure = models.CharField(max_length=50)
category_id = models.IntegerField()
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
updated_by = models.CharField(max_length=50)
updated_date = models.DateField(auto_now=True)
rule_type = models.CharField(max_length=50)
fk_tbl_rule_tbl_rule_category_id = models.ForeignKey(Tbl_rule_category,on_delete=models.CASCADE, related_name='fk_tbl_rule_tbl_rule_category_id_r')
def __str__(self):
return self.rule_name, self.closure, self.created_by, self.updated_by, self.rule_type
The error is occurring because the following is trying to add an object into an integer field: category_id=Tbl_rule_category.objects.latest('category_id')
You could just add: category_id=dataToRuleCtgry.get('category_id') or category_id=dataToRuleCtgry.category_id which will solve the error.
You also don't need to add: created_date=datetime.date.today() because your model defines auto_now=true.
As mentioned you should also amend the def __str__(self): to return a string.
https://docs.djangoproject.com/en/2.0/ref/models/instances/#django.db.models.Model.str
Alternatively
You could just add the object link directly to your foreign key for the category model.fk_tbl_rule_tbl_rule_category_id=dataToRuleCtgry. You would no longer need the integer field category_id.
It would be better practice to use the model field name category_id instead of fk_tbl_rule_tbl_rule_category_id. This would mean deleting category_id and then rename fk_tbl_rule_tbl_rule_category_id to category_id.
In Django, the ORM takes care of the basic database details for you; which means in your code you really don't have to worry about individual row ids for maintaining foreign key relationships.
In fact, Django automatically assigns primary keys to all your objects so you should concentrate on fields that are relevant to your application.
You also don't have to worry about naming fields in the database, again Django will take care of that for you - you should create objects that have fields that are meaningful to users (that includes you as a programmer of the system) and not designed for databases.
Each Django model class represents a object in your system. So you should name the classes as you would name the objects. User and not tbl_user. The best practice is to use singular names. Django already knows how to create plural names, so if you create a model class User, django will automatically display Users wherever it makes sense. You can, of course, customize this behavior.
Here is how you should create your models (we will define __str__ later):
class RuleCategory(models.Model):
name = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
class Rule(models.Model):
name = models.CharField(max_length=50)
closure = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
updated_by = models.CharField(max_length=50)
updated_date = models.DateField(auto_now=True)
rule_type = models.CharField(max_length=50)
category = models.ForeignKey(RuleCategory,on_delete=models.CASCADE)
Django will automatically create any primary or foreign key fields, and any intermediary tables required to manage the relationship between the two models.
Now, to add some records:
new_category = RuleCategory(name='My Category', created_by='XYZ')
new_category.save()
# Another way to set values
new_rule = Rule()
new_rule.name = 'Sample Rule'
new_rule.closure = closure
new_rule.created_by = 'XYZ'
new_rule.updated_by = 'XYZ'
new_rule.rule_type = rule_type
new_rule.category = new_category
new_rule.save()
Note this line new_rule.category = new_category - this is how we link two objects. Django knows that the primary key should go in the table and will take care of that automatically.
The final item is customizing the models by creating your own __str__ method - this should return some meaningful string that is meant for humans.
class RuleCategory(models.Model):
name = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
def __str__(self):
return '{}'.format(self.name)
class Rule(models.Model):
name = models.CharField(max_length=50)
closure = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
updated_by = models.CharField(max_length=50)
updated_date = models.DateField(auto_now=True)
rule_type = models.CharField(max_length=50)
category = models.ForeignKey(RuleCategory,on_delete=models.CASCADE)
def __str__(self):
return '{} for category {}'.format(self.name, self.category)
If you notice something, I just put self.category in the __str__ for the Rule model. This is because we have already defined a __str__ for the RuleCategory model, which just returns the category name; so now when we print our Rule we created, we will get Sample Rule for category My Category as a result.
I have a question concerning the following model. I want to populate the ManyToManyField from views.py instead of doing it from the Admin.
But how do I add data to the genres field which is the ManyToManyField?
views.py
content = Movie_Info(id = m_id,
title = data[0].get('title'),
overview = data[0].get('overview'),
release_date = data[0].get('release_date'),
)
content.save()
models.py
class Movie_Info_genre(models.Model):
genre = models.CharField(max_length=100)
class Movie_Info(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=100, blank=True, null=True)
overview = models.TextField(blank=True, null=True)
release_date = models.CharField(max_length=10, blank=True, null=True)
genres = models.ManyToManyField(Movie_Info_genre)
Use the add method for related fields:
# using Model.object.create is a shortcut to instantiating, then calling save()
myMoveInfo = Movie_Info.objects.create(title='foo', overview='bar')
myMovieGenre = Movie_Info_genre.objects.create(genre='horror')
myMovieInfo.genres.add(myMoveGenre)
Unlike modifying other fields, both models must exist in the database prior to doing this, so you must call save before adding the many-to-many relationship. Since add immediately affects the database, you do not need to save afterwards.
Firstly, I know how to fix the problem, I'm just trying to understand why it's occuring. The error message:
users.profile: Reverse query name for field 'address' clashes with related field 'Address.profile'. Add a related_name a
rgument to the definition for 'address'.
And the code:
class Address(models.Model):
country = fields.CountryField(default='CA')
province = fields.CAProvinceField()
city = models.CharField(max_length=80)
postal_code = models.CharField(max_length=6)
street1 = models.CharField(max_length=80)
street2 = models.CharField(max_length=80, blank=True, null=True)
street3 = models.CharField(max_length=80, blank=True, null=True)
class Profile(Address):
user = models.ForeignKey(User, unique=True, related_name='profile')
primary_phone = models.CharField(max_length=20)
address = models.ForeignKey(Address, unique=True)
If I understand correctly, this line:
address = models.ForeignKey(Address, unique=True)
Will cause an attribute to be added to the Address class with the name profile. What's creating the other "profile" name?
What if I don't need a reverse name? Is there a way to disable it? Addresses are used for a dozen things, so most of the reverse relationships will be blank anyway.
Is there a way to copy the address fields into the model rather than having a separate table for addresses? Without Python inheritance (this doesn't make sense, and if an Model has 2 addresses, it doesn't work).
in the django docs it says:
If you'd prefer Django didn't create a backwards relation, set related_name to '+'. For example, this will ensure that the User model won't get a backwards relation to this model:
user = models.ForeignKey(User, related_name='+')
but I never tried it myself....
I'm not sure where the errant profile field is coming from… But one way to find out would be: temporary remove address = models.ForeignKey(…) from Profile, ./manage.py shell, from ... import Address then see what Address.profile will tell you.
I don't think there is any official way to inherit only the fields from some other Model without using inheritance… But you could fake it like this (where SourceModel is, eg, Address and TargetModel is, eg, Profile):
for field in SourceModel._meta.fields:
TargetModel.add_to_class(field.name, copy.deepcopy(field))
(this is coming from Django's ModelBase __new__ implementation)
I don't think it's possible to disable the reverse name.
I've just done a quick grep over the code and it doesn't look like there is any logic which will bypass setting up the related_name field on the related model.
For Example: Add just '+'
class GeneralConfiguration(models.Model):
created_at = models.DateTimeField(editable=False, default=settings.DEFAULT_DATE)
updated_at = models.DateTimeField(editable=False, default=settings.DEFAULT_DATE)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.PROTECT, related_name='+')
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.PROTECT, related_name='+')