OnetoMany field in django models - django

From my search it comes to my understanding there is no OnetoMany Field in django, can someone explain or simplify a solution if i wanted to have these three classes connected to each other.
a UserRank class which i can define as many as ranks i want,example (captain,2nd eng,chief mate...etc)
a User class which can have one of the above ranks,
a job class which can have 1 or many ranks from the UserRank class
models.py
class UserRank(models.Model):
rank = models.CharField(blank=True,null=True,max_length=150)
def __str__(self):
return self.rank
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=150,unique=True)
name = models.CharField(max_length=150,)
phone = models.CharField(max_length=50)
date_of_birth = models.DateField(blank=True, null=True)
picture = models.ImageField(blank=True, null=True, upload_to='users_imgs')
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
last_login = models.DateTimeField(null=True)
user_rank = models.ForeignKey(UserRank,related_name='userRank',null=True,on_delete=models.SET_NULL)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email','name']
def get_full_name(self):
return self.username
def get_short_name(self):
return self.username.split()[0]
class Job(models.Model):
job_type = (
('I', 'Interval'),
('O', 'One time'),
)
name = models.CharField(max_length=100)
description = models.CharField(max_length=100)
type = models.CharField(max_length=1, choices=job_type)
interval = models.IntegerField()
is_critical = models.BooleanField()
due_date = models.DateField()
user_rank = models.ManyToManyField(UserRank,related_name='ranks',blank=True)
component = models.ForeignKey(
Component, related_name='jobs', on_delete=models.CASCADE)
runninghours = models.ForeignKey(
RunningHours, related_name="RHjobs", on_delete=models.CASCADE)
def __str__(self):
return self.name

You are probably looking for the "reverse relation" of a ForeignKey relation. Look at the Django documentation: Following relationships backwards
(Summary: Entry has a foreign key to Blog, and by default Blog.entry_set is a manager for related Entry objects, f.ex. Blog.entry_set.all() or any more complex Queryset to filter them. THe name entry_set is a default, you can change it via related_name on the ForeignKey in Entry)

In a one-to-many relationship, the "child" instance contains the primary key of the parent instance as a foreign key.
This means that if you have a relationship between UserRank and Job, each Job instance will have an attribute of type ForeignKey containing the primary key of the parent Job instance.
# Setup
class UserRank(models.Model):
...attributes
class Job(models.Model):
user_rank = models.ForeignKey(UserRank, on_delete=models.CASCADE, related_name="jobs")
# Accessing jobs from UserRank
user_rank_1 = UserRank.objects.create(...)
# There is one UserRank instance with many child job instances, so we have to cycle through them
for job in user_rank_1.jobs.all():
print(job)
# Result
# >>> job1
# >>> job2
# >>> job3
# Accessing UserRank from a Job instance
job = Job.objects.get(id=1)
# Every job instance only has one parent UserRank, so it's sufficient to reference it directly with the dot notation
print(job.user_rank)
# Result
# >>> user_rank_1

Related

How to use foreign key field's attribute for another model field

I have two models in different apps like so:
class Account(models.Model):
"""
Class to store fiat account information of a companies bank account
"""
number = models.CharField(max_length=100)
currency = models.ForeignKey(FiatCurrency, on_delete=models.CASCADE)
owner = models.ForeignKey(Company, on_delete=models.CASCADE)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.number
class FiatTransaction(models.Model):
"""
Class to store Transactions made between escrow and operative white-listed fiat accounts
"""
debit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='debit_account')
credit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='credit_account')
executed_on = models.DateTimeField(auto_now_add=True)
amount = models.FloatField()
currency = debit_account.currency
is_processed = models.BooleanField(default=False)
fee = models.FloatField()
memo = models.CharField(max_length=250)
def __str__(self):
return F"Transferred {self.amount} from {self.debit_account} to {self.credit_account} at {self.executed_on}"
Now the field currency of model FiatTransaction doesn't seem to work the way I intend it to do. It raises
AttributeError: 'ForeignKey' object has no attribute 'currency'
# Source model
class FiatCurrency(models.Model):
"""
A model to store Fiat Currencies offered by Finchin to
include into cash-pools.
"""
ISO_Code = models.CharField(max_length=3)
name = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
Why's that and how to make this work?
You can make a #property that will determine the currency of that object with:
class FiatTransaction(models.Model):
debit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='debit_account')
credit_account = models.ForeignKey('company.Account', on_delete=models.CASCADE, related_name='credit_account')
executed_on = models.DateTimeField(auto_now_add=True)
amount = models.FloatField()
is_processed = models.BooleanField(default=False)
fee = models.FloatField()
memo = models.CharField(max_length=250)
#property
def currency(self):
return self.debit_account.currency
This can however be inefficient if you have to do this for a lot of FiatTransactions.
In that case it might be better to remove the currency property, and annotate the QuerySet with:
from django.db.models import F
FiatTransaction.objects.annotate(currency=F('debit_account__currency'))
The FiatTransactions that arise from this will have an extra attribute named .currency that will contain the .currency of the .debit_account.
If you need this often, you can make use of a Manager that will automatically annotate when you access FiatTransaction.objects:
from django.db.models import F
class FiatTransactionManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).annotate(
currency=F('debit_account__currency')
)
class FiatTransaction(models.Model):
# …
objects = FiatTransactionManager()

django checkbox select multiple models

Hi I have the following django model:
class Issue(models.Model):
title = models.CharField(max_length=200)
date = models.DateTimeField(auto_now=True)
assignee = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assignee')
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner', null=True, blank=True)
description = models.TextField()
state = models.IntegerField(choices=STATUS_CHOICES, default=1)
priority = models.IntegerField(choices=RELEVANCE_CHOICES, default=2)
expired_date = models.DateField(auto_now=False, null=True, blank=True)
and a form which allow a user to create an Issue instance:
class IssueForm(forms.ModelForm):
class Meta:
model = Issue
fields = ('title', 'description', 'assignee', 'state', 'priority', 'expired_date')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "Titolo"
self.fields['description'].label = "Descrizione"
self.fields['state'].label = "Stato"
self.fields['priority'].label = "Priorità"
self.fields['expired_date'].label = "Termine"
self.fields['expired_date'].widget.attrs.update({'class': 'datepicker'})
self.fields['assignee'] = forms.MultipleChoiceField(
choices=self.fields['assignee'].choices,
widget=forms.CheckboxSelectMultiple,
label=("Assegnatario")
)
def clean(self):
cleaned_data = super().clean()
user_id = [i for i in cleaned_data['assignee']]
cleaned_data['assignee'] = [User.objects.get(id=i) for i in user_id]
return cleaned_data
I render this form and the field assignee is a checkbox.
I would like to be able to choose several assignee for the same issue, but I got an error because the Issue model expect just one User instance
How can I modify my model Issue in order to get more than one user ?
Thanks
you can create a new class and name it Issue_Instance where every Issue Object can have an assignee as a foreign key the problem that the relation is one to many because you have to choose more than one assignee and Django doesn't support the idea of having Array or List of Foreign Keys(I don't know any frame works that do :=) ) so I would suggest creating a new class or make the foreign key relation one-to-many key field read about it it will be very useful to solve your problem

AttributeError: 'ManyToManyField' object has no attribute 'First_Name'

i encountered the following problem when i try to migrate list of models one of which contains a ManyToMany field.
class Item(models.Model):
File "C:\Users\helin\Downloads\Django\E-commerce\Farmers\Farmersapp\models.py", line 60, in Item
sluger = farmer.First_Name
AttributeError: 'ManyToManyField' object has no attribute 'First_Name'
Below are the models i created.any help is appreciated.Thank you
class Farmer(models.Model):
id = models.AutoField(default=1,primary_key=True)
First_Name = models.CharField(max_length=15)
Last_Name = models.CharField(max_length=15)
def __str__(self):
return self.First_Name+" "+self.Last_Name
def get_farmer(self):
return self.farmer.First_Name+" " +self.farmer.Last_Name
class Item(models.Model):
id = models.AutoField(default=1,primary_key=True)
category = models.CharField(choices=CATEGORY_CHOICES, max_length=6)
price = models.FloatField()
description = models.TextField(blank=True)
image = models.ImageField()
farmer = models.ManyToManyField(Farmer, through='ItemAmount',related_name='item')
sluger = farmer.First_Name
slug = models.SlugField(default=sluger)
def __str__(self):
return self.category
class ItemAmount(models.Model):
farmer = models.ForeignKey(Farmer, on_delete=models.CASCADE)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
First, would suggest to take a look at Python style guide, like lowercase attribute names, etc.
Django auto-creates id primary-key field on every model, unless other field is set as primary-key. So, this part can be safely avoided.
get_farmer method - how is it different from str? Also, these are model instance methods ((self)), so there is no self.farmer field on Farmer object - this will fail.
class Farmer(models.Model):
# id AutoFied is created by default by django, so no need to specify
# id = models.AutoField(default=1,primary_key=True)
first_name = models.CharField(max_length=15)
last_name = models.CharField(max_length=15)
def __str__(self):
return self.first_name + " " + self.last_name
farmer = models.ManyToManyField() - as it is ManyToMany, this means many farmers, so it is better to name field farmers, also same applies to reverse relation - Farmer might have multiple items - related_name=items.
sluger - is it a field? Also, it might have many farmers, so which one to pick?
slug - referencing self fields in default is not good idea, better set default in forms.
You can make slug CharField and set its value in save() method for example.
class Item(models.Model):
# id AutoFied is created by default by django, so no need to specify
# id = models.AutoField(default=1,primary_key=True)
category = models.CharField(choices=CATEGORY_CHOICES, max_length=6)
price = models.FloatField()
description = models.TextField(blank=True)
image = models.ImageField()
farmers = models.ManyToManyField(
Farmer,
through='ItemAmount',related_name='items'
)
slug = models.SlugField()
def __str__(self):
return self.category
You can start with minimum working models and add new fields / methods one by one - it would be easier to debug and you will have base working app.

Django Nested Form - Always Showing Object instead of model details

I'm working on a Django project generated via Mezzanine. I've been able to create my models, however I want to have a form where an admin can select from a list to assign a value in a many to many or a one to many relationship. For example, I have a model for Schemas:
class Schema(AutoCreatedUpdatedMixin, SoftDeleteMixin):
"""List of all Schemas in a given database"""
name = models.CharField(max_length=128, null=False)
status = models.BooleanField(max_length=128, null=False, default=True, verbose_name="Is Active")
description = models.CharField(max_length=65535, null=True, blank=True, default=None)
database = models.ForeignKey(Database, on_delete=models.CASCADE)
pull_requests = models.ManyToManyField(Link)
questions = models.ManyToManyField(Question, blank=True)
comments = models.ManyToManyField(Comment, blank=True)
technical_owners = models.ManyToManyField(Employee, related_name='technical_owners_schemas', blank=True)
business_owners = models.ManyToManyField(Employee, related_name='business_owners_schemas', blank=True)
watchers = models.ManyToManyField(Employee, related_name='watchers_schemas', blank=True)
def __unicode__(self):
return "{}".format(self.name)
And I have a model for Employees
class Employee(AutoCreatedUpdatedMixin, SoftDeleteMixin):
"""List of people with any involvement in tables or fields: business or technical owners, developers, etc"""
name = models.CharField(max_length=256, blank=False, null=False, default=None, unique=True)
email = models.EmailField(blank=True, null=True, unique=True)
def __unicode__(self):
return "{}".format(self.employee)
An employee can own multiple schemas and a schema can be owned by multiple employees. My database has an active employee in it, however when I try to create a Schema the employee shows up as Employee Object. Rather I would want the form to show the Employee.name. How can I do this? My admin file contains the following:
class SchemasAdmin(admin.ModelAdmin):
list_display = ['name', 'status', 'database', 'description']
ordering = ['status', 'database', 'name']
actions = []
exclude = ('created_at', 'updated_at', 'deleted_at')
First of all are you using python 2 or 3? For 3, the __str__ method should be used instead of __unicode__. I am writing this because it seems that there's a problem with the __unicode__ method of Employee, which although is defined as:
def __unicode__(self):
return "{}".format(self.employee)
th Employee class does not have an employee attribute (unless there's such an attribute in the mixins that class inherits from (AutoCreatedUpdatedMixin, SoftDeleteMixin) but I don't think that is the case.
In any case, the problem is that you haven't defined a propery __str__ (if using python 3) or __unicode__ (for python 2) method on the Employee class - just define it like:
return self.name
and you should see the employee's name in the django admin select fields.

I am working with Django, During inserting data into database i caught such error

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.