I've a model with different fields.
I. E.
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
DB i.E.
id | first_name | Last_name | Instrument
1 | Stefan | Oslay | Bass
2 | Richard | Wagner | Klavier
Than I have a second model. I want to reference with foreign key the Musician. And then i want also a new custom field which contains also a attribut from Musician.
class MusikWerk(models.Model):
Interpret = models.ForeignKey(Musician, on_delete=models.CASCADE, null=FALSE, blank=False)
Title = models.CharField(max_length=50)
def instrument_wert(self)
return '%s '%(self.instrument)
instrument = instrument_wert(Musician)
DB i.E.
id | Interpretid | Title | Instrument
1 | 2 | TitelWagner | Klavier
But with my code i don't create the column Instrument in database and can't save the foreign field in them 😦
Related
I'm trying to write the following raw query with the ORM. I'm not sure is it possible or not.
select first_name,
middle_name,
COALESCE(middle_name, (
select middle_name
from contacts c2
where c2.first_name = c1.first_name
and c2.last_name = c1.last_name
and c2.middle_name is not null
order by length(c2.middle_name) desc
limit 1
)
) expected,
last_name
from contacts c1
The expected result is like the following, if middle_name is null, get the middle name from another record that has the same first_name and last_name.
id| first_name | middle_name | expected | last_name
1 | ahmet | <NULL> | burak | ozyurt
2 | ahmet | burak | burak | ozyurt
class Contact(models.Model):
first_name = models.CharField(max_length=250)
last_name = models.CharField(max_length=250, null=True, blank=True)
middle_name = models.CharField(max_length=250, null=True, blank=True)
DB: Postgres
Django Version: 3.12
By using the django ORM, you can perform the same query by using the following code
from django.db import models
from django.db.models.functions import Coalesce, Length
matched_middle_name_queryset = Contact.objects.filter(
first_name=models.OuterRef("first_name"),
last_name=models.OuterRef("last_name"),
middle_name__isnull=False,
).annotate(
middle_name_len=Length("middle_name")
).order_by("-middle_name_len").values("middle_name")[:1]
result = Contact.objects.annotate(
matched_middle_name=models.Subquery(matched_middle_name_queryset)
expected=Coalesce(
models.F("middle_name")
models.F("matched_middle_name"),
).values("id", "first_name", "middle_name", "expected", "last_name")
)
Explanations
models.OuterRef is used for referring to the field from the parent query of a subquery.
- prefix in the order_by("-middle_name_len") is for descending order
.values("middle_name") is for selecting only middle_name values.
the slicing [:1] is for limiting the result from the subquery to be one.
Tips
You can use result.query to inspect what query the ORM will generate for you.
I used python manage.py inspectdb > models.py to create models from my old SQL database. Workout.user_id is a foreign key to users table, but inspectdb lost this linking by making it just models.IntegerField().
Database looks like this:
Users
+----+------------+
| id | name |
+----+------------+
| 1 | Bob |
| 2 | Alice |
| 3 | Tom |
+----+------------+
Workout
+---------+------------+------------+
| user_id | workout_id | date |
+---------+------------+------------+
| 2 | 1 | 2021-02-18 |
| 2 | 2 | 2021-02-20 |
| 3 | 3 | 2021-02-21 |
+---------+------------+------------+
And models.py comes out like this:
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey and OneToOneField has `on_delete` set to the desired behavior
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models
class Users(models.Model):
id = models.AutoField(primary_key=True)
name = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'users'
class Workout(models.Model):
user_id = models.IntegerField()
workout_id = models.AutoField(primary_key=True)
date = models.DateField(blank=True, null=True)
class Meta:
managed = False
db_table = 'workout'
Workout.user_id is a foreign key to users table, but inspectdb lost this linking by making it just models.IntegerField().
I tried setting ForeignKey like this:
user_id = models.ForeignKey('Users', on_delete=models.CASCADE)
but it gives an error:
django.db.utils.OperationalError: (1054, "Unknown column 'workout.user_id_id' in 'field list'")
Can this kind of ForeignKey linking be achieved or how should I proceed?
Django automatically adds _id to the ForeignKey for the name of the table column, you thus name it user. You can also specify with the db_column=… parameter [Django-doc] the name of the database column, although that is not necessary here:
class Workout(models.Model):
user = models.ForeignKey(
'Users',
db_column='user_id'
)
workout_id = models.AutoField(primary_key=True)
date = models.DateField(blank=True, null=True)
class Meta:
managed = False
db_table = 'workout'
When creating a model object, can you use the verbose name to create? I have a model that has a dot in the name and I cannot have dot in a variable name.
file.xls
|---------|-----------------------|
| Start | ExonicFunc.UHNCLGGene |
|---------|-----------------------|
| 2488153 | nonsynonymous SNV |
| 3301721 | synonymous SNV |
|---------|-----------------------|
models.py
class Variant(models.Model):
start = models.CharField(max_length=10, null=True)
exonic_function = models.CharField(verbose_name="ExonicFunc.UHNCLGGene", max_length=20, null=True)
view.py
def upload(request):
# reads file.xls
raw_data = pandas.read_excel(request.FILES.get("file"))
for _, row in raw_data.iterrows():
variant = Variant.objects.create(**row)
verbose_name is just a label for your field in admin panel. You probably want to use db_column
exonic_function = models.CharField(db_column="ExonicFunc.UHNCLGGene", max_length=20, null=True)
and they assign value in your view as usual
variant.exonic_function = "bla"
I've got the following idea. One user can be in multiple companies. The user has different permissions in every company that he or she is in.
The example below doesn't work, because one user would always have the same permissions.
How should I set up my User Model that one User has a different permission in every company?
That's what I've tried so far:
class User(AbstractUser):
hourly_wage = models.DecimalField(decimal_places=2, max_digits=6, default=8.50)
class Meta:
permissions = (("is_general", "Can add people to his company and see everything"),
("is_captain", "Can add/edit/delete everything"),
("is_staff", "Can add/edit/delete jobs"),
("is_programmer", "Can do what he or she wants to do"))
def clean(self):
self.username = self.username.lower()
class Company(models.Model):
name = models.CharField(max_length=80)
slug = models.SlugField(unique=True)
users = models.ManyToManyField(User, related_name="companies")
class Meta:
verbose_name_plural = "companies"
You define a model for the ManyToManyField through which the many to many relation flows.
So instead of:
+---------+ M N +-------------+ M N +------------+
| Company |----------| User |----------| Permission |
+---------+ +-------------+ +------------+
| permissions |
+-------------+
We thus write it like:
+---------+ 1 N +-------------+ M 1 +------+
| Company |----------| UserCompany |----------| User |
+---------+ +-------------+ +------+
| M
|
| N
+------------+
| Permission |
+------------+
We can do this with:
from django.contrib.auth.models import Permission
class User(AbstractUser):
def clean(self):
self.username = self.username.lower()
class Company(models.Model):
name = models.CharField(max_length=80)
slug = models.SlugField(unique=True)
users = models.ManyToManyField(User, through='app.CompanyUser', related_name="companies")
class Meta:
verbose_name_plural = "companies"
class CompanyUser(models.Model):
user = models.ForeignKey('app.User', on_delete=models.CASCADE)
company = models.ForeignKey('app.Company', on_delete=models.CASCADE)
hourly_wage = models.DecimalField(decimal_places=2, max_digits=6, default=8.50)
permissions = models.ManyToManyField(Permission, on_delete=models.CASCADE)
You probably want to shift other logic to the through model. For example thehourly_wage is probably also related to a (User, Company)-pair, and not specific to a User itself.
You can thus add permissions to the UserCompany with:
someusercompany.permissions.add(some_permission)
Then you check if a UserCompany has a permission with:
CompanyUser.objects.filter(
user=some_user,
company=some_company,
some_permission=some_permission
).exists()
See more about this in the documentation about through parameter [django-doc].
I have a table like this:
+----+----------+----------+----------------+----------------------------------+
| id | users_id | files_id | shared_user_id | shared_date |
+----+----------+----------+----------------+----------------------------------+
| 3 | 1 | 1 | 2 | 2013-01-31 14:27:06.523908+00:00 |
| 2 | 1 | 1 | 2 | 2013-01-31 14:25:37.760192+00:00 |
| 4 | 1 | 3 | 2 | 2013-01-31 14:46:01.089560+00:00 |
| 5 | 1 | 1 | 3 | 2013-01-31 14:50:54.917337+00:00 |
Now I want to know the owner of each files who shared the file. For file 1, users_id is 1. I want to get the name of users_id 1 from the default Django auth_user table. I want to get the usernames for each file. How can I do that?
#models.py
from django.db import models
from django.contrib.auth.models import User
class File(models.Model):
users = models.ForeignKey(User)
file_name = models.CharField(max_length=100)
type = models.CharField(max_length=10)
source = models.CharField(max_length=100)
start_date = models.TextField()
end_date = models.TextField()
duration = models.TextField()
size_overview = models.IntegerField()
size = models.TextField()
flag = models.TextField()
#delete_date = models.CharField(max_length=100, null=True, blank=True)
class Share(models.Model):
users = models.ForeignKey(User)
files = models.ForeignKey(File)
shared_user_id = models.IntegerField()
shared_date = models.TextField()
class Host(models.Model):
name = models.TextField()
full_path = models.TextField()
As I've said to you before, your model structure is a bit odd - in particular, Share.shared_user should be a ForeignKey, not an IntegerField. In this case, you could help things by making users a ManyToManyField from File to User, using Share as the through table:
class File(models.Model):
users = models.ManyToManyField(User, through='Share')
Now given a File instance you can simply do:
my_file.users.count()