I have 4 models in my project. Which are :
class Company(Group):
address_1 = models.CharField(max_length = 300, blank = True, null = True)
web_site = models.URLField(blank = True, null = True)
office_number = models.CharField(max_length = 20, blank = True, null = True)
class Person(models.Model):
user = models.ForeignKey(User)
company = models.ForeignKey(Company)
class Project(models.Model):
name = models.CharField(max_length = 100)
person = models.ManyToManyField(User, through = 'UserProject')
class UserProject(models.Model):
user = models.ForeignKey(User)
project = models.ForeignKey(Project)
is_owner = models.BooleanField(default = False)
In a view I would want to get
All the projects related to the request.user
The companies that are working on those projects
and the employees of those companies
I have tried writing some code but the queries are not precise. Help would be appreciated!
All projects related to request.user is easy, of course:
Project.objects.filter(person=request.user)
The companies working on those projects necessarily requires you to loop through the projects:
for p in projects:
p.company_set.all()
You have no relationship between User and Company, or any other foreign key on Company for that matter, so I have no idea where the concept of "employee" comes from or how to get that.
Since, this is pretty basic stuff, I'm assuming your issue is with the fact 1*N queries that are generated. In the current version of Django (1.3.1), there's no way to optimize this futher. In Django 1.4, there's prefetch_related that will allow you to select all the companies (and employees) with the Projects. However, it still requires a unique query for each relationship, so 3 total to get projects, companies, and employees. It works like this:
Project.objects.filter(person=request.user).prefetch_related('company')
In the mean time, I've had some success with using django-batch-select, which basically tries to emulate the behavior of prefetch_related.
OK some bugs in your models:
Project has a M2M on User through UserProject, which is fine, except I think you mean for it to have a M2M on Person which has a FK to User
Secondly, you haven't set up any relations to Company. there is no way to do item 2 on your list until that bug is fixed.
Why is Company extending Group? I must be missing something.
There is no Employee model
Example:
class Company(models.Model):
address_1 = models.CharField(max_length = 300, blank = True, null = True)
web_site = models.URLField(blank = True, null = True)
office_number = models.CharField(max_length = 20, blank = True, null = True)
class Employee(models.Model):
user = models.ForeignKey(User)
company = models.ForeignKey(User)
class Project(models.Model):
name = models.CharField(max_length = 100)
employees = models.ManyToManyField(Employee, through = 'EmployeeProject')
companies = models.ManyToManyField(Company)
class EmployeeProject(models.Model):
employee = models.ForeignKey(Employee)
project = models.ForeignKey(Project)
is_owner = models.BooleanField(default = False)
and in the view
# all of the projects for a user (assuming employee field is supposed to M2M to the Employee model
projects = Project.objects.filter(employees__user=request.user)
for project in projects :
# assuming that there was some connection between project and company (there isnt currently, see me list of bugs with your models)
for company in project.companies_set.all() :
# There is no employee model, but if there was
employees = company.employees_set.all()
I have pondered and used some of the solutions enlisted above, but nothing was exact to what I was looking for. For me the following piece of code works well
projects = Project.objects.filter(person = request.user)
user_projects = UserProject.objects.filter(project__in = projects)
for user_project in user_projects:
person = user_project.user.get_profile()
company.append(person.company)
people.append(person)
company = set(company)
Related
I am working on a Django project and stuck at a problem. My models look like this:
class Products(models.Model):
basket_name = models.CharField(max_length = 5, blank = False)
product_name = models.CharField(max_length = 30, blank = False)
quantity = models.PositiveSmallIntegerField(null = False, blank = False, default=1)
class ShelfProduct(models.Model):
shelf_name = models.CharField(max_length = 15, default="defaultshelf")
products = models.ManytoManyField(Products)
..... other fields....
class KioskShelf(models.Model):
kiosk_name = models.CharField(max_length= 15, default="default")
shelfproduct = models.ManytoManyField(ShelfProduct)
...other fields....
class MapperModel(models.Model):
kioskshelf = models.ForeignKey(KioskShelf, on_delete = models.CASCADE)
...other fields....
I am trying to update the product quantity in Products models for a particular kiosk name and particular shelf. I tried like this:
data = MapperModel.objects.filter(kioskshelf__kiosk_name = 'kiosk1').filter(kioskshelf__shelfproduct__shelf_name = 'Shelf A')
But after this am not sure how to update the quantity in Products table. Am not even so sure if my approach is correct. Please assist me how to do it. Thanks a lot in advance.
You need to go through the Products table. Have you tried:
Products.objects.filter(shelfproduct__shelf_name='Shelf A', shelfproduct__kioskshelf__kiosk_name='kiosk1').update(quantity=<quantity>)
I am trying to enable post methods in my borrowedview to enable admins, select from current users, and also select from the available books.
serializers.py
class BookListSerializer(serializers.ModelSerializer):
authors = serializers.SlugRelatedField(many = True, queryset = models.Author.objects.all(),slug_field = 'name',) #To represent the relationship as a string instead of id
genre = serializers.SlugRelatedField(many = True,queryset = models.Genre.objects.all(),slug_field = 'name')
class Meta:
model = models.Book
fields = ('name','authors','rating', 'genre')
class BorrowedSerializer(serializers.ModelSerializer):
who_borrowed = serializers.PrimaryKeyRelatedField(queryset = get_user_model().objects.all() ,source = 'who_borrowed.username',)
name = serializers.PrimaryKeyRelatedField(queryset = models.Book.objects.all(),
source = 'name.name',)
#borrowed_date = serializers.DateTimeField(format = "%H:%M, %d-%m-%Y")
#returned_date = serializers.DateTimeField(format = "%H:%M, %d-%m-%Y" )
class Meta:
model = models.Borrowed
fields = ('who_borrowed','name','has_returned','borrowed_date','returned_date',)
Then my models
models.py
class Book(models.Model):
name = models.CharField(max_length = 150,) #Name of books
authors = models.ManyToManyField(Author,)
genre = models.ManyToManyField(Genre)
publisher = models.ForeignKey(Publisher, on_delete = models.SET_DEFAULT, default = '2')
#A book should have a publisher will include a none field for books without publishers
pub_date = models.DateField()
price = models.DecimalField(max_digits = 10, decimal_places = 2)
isbn = models.CharField(max_length = 13, unique = True, null = True, blank = True,
validators = [MinLengthValidator(13)])
class Borrowed(models.Model): #Model for users borrowing and returning
name = models.ForeignKey(Book, on_delete = models.CASCADE,) #Consider making one to many field
borrowed_date = models.DateTimeField(auto_now_add = True, ) #Date is created as soon as instance is created
has_returned = models.BooleanField(default = False) #Field that determines if a model is returend or not
returned_date = models.DateTimeField(null = True, blank = True,) #Date that changes as soon as book is returned
who_borrowed = models.ForeignKey(get_user_model(), on_delete = models.SET_DEFAULT, default ='9c495b90-3900-43d1-875d-6b15d5d5ab55')
The Django browseable api shows the books to choose from and the user borrowing the book just as I want but when I want to post the request it shows the error from the title.
I don't know how to make a create method that would allow those options to show and would allow the post or put requests.
In a nutshell if I remove the source argument everything works fine, but I want the data shown not to be the Id or the UUID but the usernames and the names of the books instead.
You can refer DRF- doc.
Following changes should be enough in your case.
class BorrowedSerializer(serializers.ModelSerializer):
who_borrowed = serializers.PrimaryKeyRelatedField(queryset = get_user_model().objects.all() ,source = 'who_borrowed.username',)
name = serializers.PrimaryKeyRelatedField(queryset = models.Book.objects.all(),
source = 'name.name',)
class Meta:
model = models.Borrowed
fields = ('who_borrowed','name','has_returned','borrowed_date','returned_date',)
def create(self, validated_data):
return models.Borrowed.objects.create(**validated_data)
# or
# return super(BorrowedSerializer, self).create(validated_data)
class BorrowedSerializer(serializers.ModelSerializer):
who_borrowed = serializers.SlugRelatedField(queryset = get_user_model().objects.all(),slug_field = 'username')
name = serializers.SlugRelatedField(queryset = models.Book.objects.all(), slug_field = 'name')
Changing the "PrimaryKeyRelatedFields" to "SlugRelatedFields" also worked for me.
The message of the error the create method doesn't support writable dotted source fields by default is refering to name.name in the source argument of the customized serializer field,
so other ways to solve this are :
1. add a readonly=True in your new field
2. delete the customized field, so you get back to the id instead of the name.
EDIT 22/01/2021
Thanks for advices in models naming.
Here is diagram of my database: https://dbdiagram.io/d/5fd0815c9a6c525a03ba5f6f?
As you can see in this diagram, I have simplyed as Customers_Orders is in fact a ternary relationship with models comments. I decided to use an 'declared' throught models for this ternary relationship
Do I realy need to add a ManyToMany fields in Orders?
I have a look at Django's doc example with person, group and membership (https://docs.djangoproject.com/fr/3.1/topics/db/models/) and a manytomany fiels is added only in Group model, not in Person model
I have 2 models (Customers and Orders) with a declared throught models (Customers_Orders) to manage manytomany relationship.
But I did'nt understand how to query to have :
for one Customer, all its orders: How many orders were made by each customer
for one Order, all its customers: How many customers were associated for each order
I have understood, I should do:
c = Customers.objects.get(customer_id=1)
c.CustomersOrders.all() but it failled AttributeError: 'Customers' object has no attribute 'CustomersOrdersComments'
class Customers(SafeDeleteModel):
customer_id = models.AutoField("Customer id", primary_key = True)
orders = models.ManyToManyField(Orders, through = Customers_Orders, related_name = "CustomersOrders")
created_at = models.DateTimeField("Date created", auto_now_add = True)
class Orders(SafeDeleteModel):
order_id = models.AutoField("Order id", primary_key = True)
created_at = models.DateTimeField("Date created", auto_now_add = True)
class Customers_Orders(SafeDeleteModel):
order = models.ForeignKey("Orders", on_delete = models.CASCADE)
customer = models.ForeignKey("Customers", on_delete = models.CASCADE)
You can do this - Given these models:
class Customer(models.Model):
name = models.CharField(max_length=30, default=None, blank=True, null=True)
orders = models.ManyToManyField("Order", through="CustomerOrder", related_name="orders")
created_at = models.DateTimeField(auto_now_add=True)
class Order(models.Model):
created_at = models.DateTimeField(auto_now_add = True)
customers = models.ManyToManyField(Customer, through="CustomerOrder", related_name="customers")
class CustomerOrder(models.Model):
order = models.ForeignKey("Order", on_delete = models.CASCADE)
customer = models.ForeignKey("Customer", on_delete = models.CASCADE)
customer = Customer.objects.first()
# Get count of their orders
customer_orders_count = customer.orders.all().count()
order = Order.objects.first()
# Get count of order's customers
order_customers_count = order.customers.all().count()
The docs explains this quite well:
https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/
In your case, that would be something like:
customer = Customers.objects.get(customer_id=1) # Fetch a specific customer from DB
customer_orders = customer.orders.all() # Return a QuerySet of all the orders related to a given `customer`
c.CustomersOrders.all() can't work, because CustomersOrders is the class name of your through model, and your Customers model does not have any CustomersOrders field.
I am trying to create a Django app where there are two main models - Team and Person.
A Person can use the app as an User or can belong to a Team, and, in that case, the User will be the Team.
class Team(models.Model):
name = models.CharField(max_length=50)
team_user = models.ForeignKey(
User,
on_delete = models.CASCADE,
null = True,
blank = True )
class Person(models.Model):
person_user = models.ForeignKey(User, on_delete = models.CASCADE, null = True, blank = True)
team = models.ForeignKey(
Team,
on_delete = models.CASCADE,
null = True,
blank = True )
I am trying to develop a File structure like this:
/root
/Team_01
/Person_01 (non-User)
/files.csv
/Person_02 (non-User)
/files.csv
/Person_03 (User)
/files.csv
I have read the documentation and I was trying to do something with the comment from Evgeni Shudzel in this post, but my problem is that I don't know how to set the file's path for the Person model so that if the Person is member of a Team, the path is "/team_id/person_id" and if not, the "/team_id" part is excluded. The pseudocode I have in mind:
if Person.team exists:
files = models.FileField(upload_to="TEAM_ID/PERSON_ID/")
else:
files = models.FileField(upload_to="PERSON_ID/")
How can I implement this pseudocode? Thank you!
You are on correct path. upload_to can point to a function.
def user_directory_path(instance: Person, filename):
# file will be uploaded to MEDIA_ROOT / your path
if instance.team: #
return f"team_{instance.team.id}/user_{instance.id}"
else:
return f"person_{instance.id}"
# ...
class Person(models.Model):
files = models.FileField(upload_to = user_directory_path)
I have a Django models where I have this :
class Patient(models.Model):
FirstName = models.CharField(max_length=264)
LastName = models.CharField(max_length=264)
Address = models.TextField(blank=True)
Telephone_no = models.CharField(max_length=100,blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name='patients')
class UploadedImages(models.Model):
patient = models.ForeignKey(Patient,on_delete=models.CASCADE,related_name='images')
original = models.ImageField(upload_to = user_directory_path, validators=[validate_file_extension],verbose_name = 'Image')
enhanced = models.ImageField(upload_to=analyses_directory_path, blank=True)
segmented = models.ImageField(upload_to=analyses_directory_path, blank=True)
class Processed(models.Model):
uploaded_image = models.ForeignKey(UploadedImages,on_delete=models.CASCADE,related_name='processed')
pre_analysed = models.ImageField(upload_to=analyses_directory_path, blank=True)
analysedimage = models.ImageField(upload_to=analyses_directory_path, blank=True)
so I want to make queries based on the current user which is user = request.user this is possible in the patient model case as I can make Patient.objects.filter(user=user)
but i can't make it the other 2 models
is there any idea how I can do this?
I didn't add the user FK as I thought I wouldn't need it but now I do?
do i need to add it ? can I make a query without adding the field ?
If you want to query across relationships, Django has explicit syntax for that. For example, to get all the UploadedImage objects for a specific user, use UploadedImage.objects.filter(patient__user=user).
Patient.objects.filter(user=user) returns a queryset, to get patient by user, assuming one Patient has only one user:
patient = Patient.objects.filter(user=user).first()
then you can do:
uploaded_images = patients.images.all()
for image in uploaded_images:
processed = image.processed.all()