Linking two models automatically in django - django

I would like when i create new order to be linked to this company. now, i have to choose it manually
class Company(models.Model):
name = models.CharField(max_length=64)
address = models.TextField(max_length=250)
class Order(models.Model):
company = models.ForeignKey('Company', on_delete=models.CASCADE)
order_date = models.CharField(max_length=64)
order_notes = models.TextField(max_length=250)

First of all if every Order is connected to this particular Company creating Foreign Key is overintended. If you still want to do this for some reason here is the solution.
class Order(models.Model):
company = models.ForeignKey('Company', on_delete=models.CASCADE)
order_date = models.CharField(max_length=64)
order_notes = models.TextField(max_length=250)
def save(self, *args, **kwargs):
# u have to have a new order in db
super().save(*args, **kwargs)
# then assing this particular company
self.company = Company.objects.get(name='the_company_name', address='the_company_address')
# and again save the changes
super().save(*args, **kwargs)
but if you want to do this this way consider making the Company name and address unique_together

Related

Import-Export with "multilevel" models

I am figuring how can I manage this situation with django-import-export using the same excel and differents models with differents djangoapps. I have the following models:
# employees/models.py
class Employee(models.Model):
name = models.CharField(max_length=100)
job = models.ForeignKey(Job, on_delete=models.SET_NULL, null=True, blank=True)
# jobs/models.py
class Job(models.Model):
value = models.CharField(max_length=100)
department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True, blank=True)
place = models.ForeignKey(Place, on_delete=models.SET_NULL, null=True, blank=True)
class Department(models.Model):
value = models.CharField(max_length=100)
business = models.ForeignKey(Business, on_delete=models.SET_NULL, null=True, blank=True)
class Place(models.Model):
value = models.CharField(max_length=100)
business = models.ForeignKey(Business, on_delete=models.SET_NULL, null=True, blank=True)
class Business(models.Model):
name = models.CharField(max_length=100)
On excel I have following values:
xls_employee_name, xls_employee_job, xls_business_name
Jon Doe, Web Developer, Company 1
I know how to import employee name and his job because Job is directly related by ForeignKey. But how can I import business name?
Below is the code for employee name and his job:
# employees/resource.py
class EmpleadoResource(resources.ModelResource):
name = fields.Field(attribute='nombre', column_name='Nombre')
job = fields.Field(
column_name='xls_employee_job',
attribute='job',
widget=ForeignKeyWidget(
Job,
field='value'
))
class Meta:
model = Employee
fields = ('name','job',)
skip_unchanged = True
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
Is it possible to import business name using one excel? I appreciate if someone could guide me. I'm also pretty new with django.
thank you for all your answers. I finally manage it and this is the solution(it was a little trickier for me, but very simple. I tried to translate all spanish names to english, sorry if I misslooked some):
# employees/resource.py
class EmployeeResource(resources.ModelResource):
name = fields.Field(attribute='name', column_name='Name')
job = fields.Field(
column_name='xls_employee_job',
attribute='job',
widget=ForeignKeyWidget(
Job,
field='value'
))
place = fields.Field(attribute='place', column_name='xls_place_column')
department = fields.Field(attribute='department', column_name='xls_department_column')
business = fields.Field(attribute='business', column_name='xls_business_name')
class Meta:
model = Employee
fields = ('name','job','place','department','business')
skip_unchanged = True
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
self.place = row["xls_place_column"]
self.department = row["xls_department_column"]
self.business = row["xls_business_name"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
Centro.objects.get_or_create(name=self.centro)
Departamento.objects.get_or_create(name=self.departamento)
Empresa.objects.get_or_create(nombre=self.empresa)
I was stucked using widgets for business, department and place, but it was not necessary
Option 1
You can assign the column value for each row to a temporary attribute on the model (it doesn't matter that this attribute is not in the Employee model):
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
# assign row value to a temporary attr
self.business = row["xls_business_name"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
# create instance using temp attr value
Business.objects.get_or_create(name=self.business)
Option 2
You can create all Business instances by processing the dataset in a batch before processing the Employee resources:
def before_import(self, dataset, using_transactions, dry_run, **kwargs):
for row in dataset.dict:
business = row["xls_business_name"]
Business.objects.create(name=business)
Note that you could use bulk_create() here to make this process more efficient.
Add transaction protection as you see fit, and bear in mind that the Business and Job entities will be created even if your import fails.

Django - Adding up values in an object's many to many field?

I have 2 classes
class Service(models.Model):
service_name = models.CharField(max_length=15, blank=False)
service_time = models.IntegerField(blank=False)
class Appointment(models.Model):
name = models.CharField(max_length=15)
service_selected = models.ManytoManyField(Service, blank=True)
total_time = models.IntegerField(null=True, blank=True)
What Im trying to do is that after a user has selected the services and created an appointment, each of the services' service_time will be added up to equal total_time
I know I have to use something along the lines of
#in class Appointment
def save(self, *args, **kwargs):
self.total_time += self.service_selected.service_time #it needs to add up all of the service_time from each service selected but that's where Im lost
super(Appointment, self).save(*args, **kwargs)
But I don't know how to get the service_time value from each of the service_name that were selected, or how to query for every service_selected chosen in that appointment's object so that I can add up those values.
edit:
Had to change
total_time = models.IntegerField(blank=False)
to
total_time = models.IntegerField(blank=False, null=False, default=0)
for the answer to work
You can do it as follows:
#in class Appointment
def save(self, *args, **kwargs):
self.total_time += Service.objects.all().aggregate(total_time=Sum('service_time'))['total_time']
# You will have to run a query to get the data and aggregate according to that. You may change the query according to your needs.
super(Appointment, self).save(*args, **kwargs)

Limiting the number of entries to be added to a Django model with a Foreign Key

I am creating a real estate system using Django. I have Property models where the number of available units is defined.
I have a separate Model for units, where each unit of a property is defines with its unique characteristics.
I want to limit the number of units to be added not to exceed the number of units as defined in the Property Model.
How do I accomplish that.
The Property model is as shown below:
# property
class Property(models.Model):
name = models.CharField('Property Name', max_length=100, unique=True)
owner = models.ForeignKey(PropertyOwner, on_delete=models.CASCADE)
location = models.ForeignKey(Location, on_delete=models.CASCADE)
type = models.ForeignKey(PropertyType, on_delete=models.CASCADE)
no_of_units = models.IntegerField('Number of Units')
date_added = models.DateField('Date added')
The unit model is as defined:
class Unit(models.Model):
property = models.ForeignKey(Property, on_delete=CASCADE)
name = models.CharField(max_length=10)
You can override save method in Unit model to prohibit creating more than N relations:
class Unit(models.Model):
property = models.ForeignKey(Property, on_delete=CASCADE)
name = models.CharField(max_length=10)
def save(self, *args, **kwargs):
max_units = self.property.no_of_units
if self.property.unit_set.count() > max_units :
raise Exception(f'Cannot add more than {max_units} relations')
super().save(*args, **kwargs)
Edit: Didn't notice no_of_units field
This should work
from django.core.exceptions import ValidationError
class Unit(models.Model):
property = models.ForeignKey(Property, on_delete=CASCADE)
name = models.CharField(max_length=10)
def save(self, *args, **kwargs):
allowed_units = self.property.no_of_units
if Unit.objects.filter(property=self.property).count() >= allowed_units:
raise ValidationError('Maximum units limit exceed!')
super(Unit, self).save(*args, **kwargs)

Django model audit mixin

Hello I wanted to know how to create a few fields and convert them into a mixin.
Let's say I have the following.
class Supplier(models.Model):
name = models.CharField(max_length=128)
created_by = models.ForeignKey(get_user_model(), related_name='%(class)s_created_by')
modified_by = models.ForeignKey(get_user_model(), related_name='%(class)s_modified_by')
created_date = models.DateTimeField(editable=False)
modified_date = models.DateTimeField()
def save(self, *args, **kwargs):
if not self.id:
self.created_date = timezone.now()
self.modified_date = timezone.now()
return super(Supplier, self).save(*args, **kwargs)
I want to create a mixin to avoid writing every time the last 4 fields into different models.
Here is the mixin I would create:
class AuditMixin(models.Model):
created_by = models.ForeignKey(get_user_model(), related_name='%(class)s_created_by')
modified_by = models.ForeignKey(get_user_model(), related_name='%(class)s_modified_by')
created_date = models.DateTimeField(editable=False)
modified_date = models.DateTimeField()
def save(self, *args, **kwargs):
if not self.id:
self.created_date = timezone.now()
self.modified_date = timezone.now()
return super(Supplier, self).save(*args, **kwargs)
class Supplier(AuditMixin):
name = models.Charfield(max_length=128)
How can I make sure that the related_name is relevant to the class the mixin is included into? Also in the save function, How can I make sure the class the mixin is included into is returned (as per the last line)?
Thank you!
Firstly, in any super call, you must always use the current class. So it will always be super(AuditMixin, self)... and your question does not apply.
Django itself takes care of substituting the current class name in related_name if you use the %(class)s syntax, which you have, so again there is nothing else for you to do. See the model inheritance docs.

Django based marketplace, creating a transaction history

I'm trying to create a transaction history for each transaction on a Django based marketplace.
I thought the best way of keeping track of this data was to override the save() function and create a Transaction record.
class Transaction(models.Model):
item = models.ManyToManyField(Item, blank=True)
buyer = models.ManyToManyField(User, related_name='buyer')
seller = models.ManyToManyField(User, related_name='seller')
description = models.CharField(max_length=500)
purchase_date = models.DateField(auto_now_add=True)
value = models.DecimalField(max_digits=7, decimal_places=2)
def save(self, *args, **kwargs):
self.buyer.money+=self.value
self.seller.money-=self.value
super(Transaction, self).save(*args, **kwargs)
Am I going about this all wrong? Currenlty I get...
'Transaction' instance needs to have a primary key value before a many-to-many relationship can be used.
You have to save your object before you can go through many-to-many relationships.
Please explain how you can have multiple buyers and sellers on a single transaction. (For the rest of this answer, I'm assuming that there aren't and you meant for these to be ForeignKey fields.)
The related names for buyer and seller are not clear. See below.
I'm not sure what description is for. Is it different from the item list?
item should be called items, since it can be plural, and you might want to create a custom junction table (using the "through" parameter) with a quantity field.
You forgot to save the related objects.
Modified version:
class Transaction(models.Model):
items = models.ManyToManyField(Item, through='TransactionItem', blank=True)
buyer = models.ForeignKey(User, related_name='transactions_as_buyer')
seller = models.ForeignKey(User, related_name='transactions_as_seller')
description = models.CharField(max_length=500)
purchase_date = models.DateField(auto_now_add=True)
value = models.DecimalField(max_digits=7, decimal_places=2)
def save(self, *args, **kwargs):
super(Transaction, self).save(*args, **kwargs)
self.buyer.money += self.value
self.buyer.save()
self.seller.money -= self.value
self.seller.save()
class TransactionItem(models.Model):
transaction = models.ForeignKey(Transaction)
item = models.ForeignKey(Item)
quantity = models.IntegerField()
The buyer and seller fields are many to many fields so self.buyer will never work, I think you were meaning to use ForeignKey instead of ManyToManyField.