I have general question. Let’s say I have a model field that is the sum of two other fields in a different model.
I’m having a hard time to implement this.
Let’s take the following example
model1
field1
field2
model2
field3 (dependent on field1 and field2) in model1
If I do it as part of specific page in my webapp. It means that if field1 or field2 has changed but the person didn’t visit the page that sum up the value and update it in field3 then field3 will carry incorrect value.
The only way to takle such a problem that I managed to identify is to never create field3. everytime a sum(or any other operation that had dependency on other fields) take a place is to be done in a variable inside the view.py
This means that value to be calculated everytime it is needed.
This way I won’t get myself in a position where I forget to recalculate the value of field3.
My question is this the best way to do it? Is there a way that whenever a depedent field change such as field1 that automatically change field3 without the need to visit a specific page?
I tried something with foriegn keys for field3 and try to add the value of two foriegn key inside the model.py but I don’t think it is allowed.
field3 = field1+ field2
any suggestions?
**
added the following example per request to further clarify question
If you notice that the totalPrice under transaction table is based on the price for the item and shipping. However, this require visiting order.html page.
My question if someone changed the item that resulted in a different price. Then without visiting the order.html page the totalprice in transaction table won't reflect the new price. Is there a way to build the "transaction" model in a way that updates the totalprice if any other field it depends on was updated without the need to visit the order.html page?
**
models.py
class Item(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
price = models.FloatField(null=True)
class Shipping(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
price = models.FloatField(null=True)
class Transaction(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
total_price = models.FloatField(null=True)
views.py
def order(request):
item_obj = item.object.get(user=self.user)
ship_obj = shipping.object.get(user=self.user)
trans_obj = transaction.object.get(user=self.user)
trans_obj.total_price = item_obj.price + ship_obj.price
trans_obj.save()
return render(request, 'order.html')
You can override save function in Shipping and Item models.
def save(self, *args, **kwargs):
#change total_price
# Transaction.save(update_fields['total_price'])
super(Item, self).save(*args, **kwargs)
However "#change total_price" will depend on how you are going to relate your models and how you're going to find proper Shipping object
Related
I have the following Models of customer details with each customers having different payment modes (eg. cash, online transfer, etc...) :
class Customer(models.Model):
#some customer details
class Payment(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.RESTRICT, related_name='payments')
payment_mode = models.CharField(max_length=255, blank=True, null=True)
And I would like to add a new Invoice Model to include the customer as well as the customer's payment modes.
class Invoice (models.Model):
customer = models.ForeignKey(Customer, on_delete=models.RESTRICT, related_name='payments')
payment_mode = models.ForeignKey(Customer.payments.payment_mode, on_delete=models.RESTRICT)
I am going to create an Invoice for a customer and will input the customer's available payment mode. But the the invoice's payment mode is giving me an AttributeError: 'ReverseManyToOneDescriptor' object has no attribute 'payment_mode'.
May I know how do I set up the reference to the customer's child data?
Thank you.
What you have is not a valid field definition.
You can reference Payment directly
payment_mode = models.ForeignKey(Payment, on_delete=models.RESTRICT)
Why is the Invoice model even required? You have the same fields already in the existing models.
You can simply make a view to do this.
For example,
def show_invoice(request, pk):
# this pk value should be sent by your frontend and represents your Primary Key value for the current customer
customer = Customer.objects.get(pk=pk)
return render(request, 'some_html_page.html', {'customer': customer})
Now in your some_html_page.html, you can show the details you want using the 'context' to the render function ({'customer': customer}) we just passed.
Models in Django should be thought of as Tables in SQL; you are not supposed to make a new one every time you want to "infer" something, rather you should make them when you want to 'store' something (or normalize existing tables/models). The Invoice model does not do either of these two things.
You can give this a read to understand how to write views in Django this.
In the case where you really want to make an Invoice model you don't need to make a payment_mode field, since you're already pointing to the Customer model which is, in turn, pointing to the Payment model.
I have created a model called Department, Course. Models are as follow
This is the model for departments and course
class Departments(models.Model):
Department_Id = models.IntegerField(primary_key=True)
Department_Name = models.CharField(max_length=200)
Department_Code = models.CharField(max_length=200)
class Course(models.Model):
Course_Id = models.IntegerField(primary_key=True)
Department_Id = models.ForeignKey(Departments, on_delete=models.CASCADE)
Course_Name = models.CharField(max_length=200)
Course_Code = models.CharField(max_length=200)
I want to create a model called view which can be later on called for search. I want a view model in a such a way that it consit of the data in concat form i.e. name= Department_name+ Course_Name
class View (models.model):
view_id= models.IntegerField(primary_key=True)
Name= Department_name(I want this from Departments table)
+ Course_Name(I want this from Course table)
I try using one to one relation . I would really appricate the help
It's not clear why you'd want to do that. It's never a good idea to duplicate data from one model into another one, as it can lead to inconsistencies.
You can add a ForeignKey in View to your Course model and then when you do f"{view.course.name} {view.course.department.name}" you already have your string:
class View(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
def name(self):
return f"{self.course.name} {self.course.department.name}"
Notes:
Don't call your foreign key Department_id because it's not referring to the id but to the object itself in the Django ORM: department = models.ForeignKey(Department, on_delete=models.CASCADE). As you can see, this makes reading the code much simpler: self.course.Department_id is a Department object not an integer, so self.course.department makes more sense.
Don't prefix your field names with the class, it just makes the code so much less readable: Do you prefer department.name or department.Department_name?
The View model is still a mystery to me, as you can search without it. You can search for example for courses with a matching department name like this:
Course.objects.filter(department__name__icontains="maths")
which will return all courses with "maths" in their department name.
Remove all the ids from your models, they are created automatically by Django anyway (and called id). Again, department.id is much easier to read than department.Department_id. Also in your code, you have to generate the ids yourself since you don't set them to auto-populate.
I have been trying to figure out the best way (or correct) way to set up models for our PIM/PriceModel app in Django.
Example models (stripped):
class ProductItem(models.Model):
"""
An actual item/product, what it all revolves around.
"""
part_number = models.CharField(unique=True, max_length=50, help_text='')
internal_part_number = models.CharField(primary_key=True, max_length=50, help_text='') # prefilled by partnumber
type = models.ForeignKey('Type', null=True, on_delete=models.SET_NULL)
attributes = JSONField() # Or another dynamic field
# and more ....
class Type(models.Model):
"""
Product type i.e. camera-dome, camera-bullet, pir, etc.
"""
pass
class Segment(models.Model):
"""
A segment of the company like Industry, Retail, Guarding etc.
"""
pass
class ProductCategory(models.Model):
"""
Supposedly a child category of TopCategory.
"""
pass
class InstallPrice(models.Model):
"""
Product item installation prices based on Type, Product Category and Segment.
"""
install_price = models.DecimalField(max_digits=8, decimal_places=2, help_text='')
type = models.ForeignKey('Type', null=True, on_delete=models.SET_NULL)
product_category = models.ForeignKey('ProductCategory', null=True, on_delete=models.SET_NULL)
segment = models.ForeignKey('Segment', null=True, on_delete=models.SET_NULL)
Take a look at "attributes = HStoreField(db_index=True)" in the ProductItem model.
The main thing i need to store in a product item is attributes like how many inputs/outputs/connection-options does it have. I need to store this for testing products against each other further down the line in the price-model app. This to make sure you have the right amount of products with matching attributes like inputs or outputs. I also need the User/Admin to be able to add this attributes dynamically so the app becomes self sustainable and not requires a migration id there is a new attribute I dont yet know about.
As I could not figure out a reasonable model configuration I ended up looking at postgres specific fields. This is not a must!
ideally when selecting type in the admin section i would like a "preset" of attributes to be presented based on the type.
Attributes could be:
inputs # number
outputs # number
channels # number
analysis # Boolean
Is this achievable? Any suggestions are welcome as I have limited Data Base experience. I need help figuring out the models.
I have a situation where I need to do something similar to rendering a formset within a formset. But I'd rather focus on the problem before jumping to a solution.
In English first:
I'm creating a shipment from a warehouse.
Each shipment can contain multiple lines (unique combinations of product_type and package_type) with an item_count
However for each line there could be multiple "Packages" - a package_type of a product_type that has an item_count. Think of this as a batch.
The customer is only interested in seeing one line for each product_type/package_type
But we need to pull out the stock and correctly attribute the particular units from each batch to allow stock control, recall control etc to function. Therefore the dispatch staff IS interested in exactly which Packages are shipped.
Add to this the sales staff enter a SalesOrder that only specifies the product_type/package_type. They aren't interested in the Packages either. (Think putting in a forward order for next month - who knows what will be in stock then?).
Now the models (simplified for clarity):
class Package(models.Model):
create_date = models.DateField()
quantity = models.FloatField()
package_type = models.ForeignKey(PackageType, on_delete=models.PROTECT)
product_type = models.ForeignKey(ProductType, on_delete=models.PROTECT)
class CheckOut(models.Model):
package = models.ForeignKey(Package, on_delete=models.PROTECT)
create_date = models.DateField()
quantity = models.FloatField()
class Shipment(models.Model):
sales_order = models.ForeignKey(SalesOrder, null=True, blank=True)
ship_date = models.DateField(default=date.today,
verbose_name='Ship Date')
class ShipmentLine(models.Model):
shipment = models.ForeignKey(Shipment, null=True, blank=True)
sales_order_line = models.ForeignKey(SalesOrderLine, null=True, blank=True)
quantity = models.FloatField(verbose_name='Quantity Shipped')
checkout = models.ManytoManyField(CheckOut)
I currently have it working well with the constraint of a 1:M relationship of CheckOut:ShipmentLine. However when changing this to a M:M, things get knarly form-wise.
In the 1:M version the Shipment form (plus formset for the ShipmentLines) looks like this:
class CreateShipmentForm(forms.ModelForm):
class Meta:
model = om.Shipment
contact = forms.ModelChoiceField(
queryset=om.Contact.objects.filter(is_customer=True, active=True),
label='Customer')
customer_ref = forms.CharField(required=False, label='Customer Reference')
sales_order = forms.ModelChoiceField(queryset=om.SalesOrder.objects.all(),
required=False, widget=forms.HiddenInput())
number = forms.CharField(label='Shipment Number', required=False,
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
class CreateShipmentLineForm(forms.ModelForm):
class Meta:
model = om.ShipmentLine
widgets = {
'checkout': forms.HiddenInput()
}
fields = ('package', 'quantity', 'id',
'sales_order_line', 'checkout')
id = forms.IntegerField(widget=forms.HiddenInput())
sales_order_line = forms.ModelChoiceField(
widget=forms.HiddenInput(), required=False,
queryset=om.SalesOrderLine.objects.all())
package = forms.ModelChoiceField(required=True, queryset=None) # queryset populated in __init__, removed for brevity
So for the 1:M, I could select a package, set the quantity and done.
For M:M, I will need to select product_type, package_type, and then 1 or more packages, AND for each package a quantity. (I'll be using JS in the form to filter these)
In my mind's eye I have a few possibilities:
create a (child) formset for the Packages and quantities and include in each line of the (parent) formset
create some sort of multi-field, multi-value matrix custom form field and use that
construct a modal dialog where the M:M stuff happens and somehow save the result to the form where validation, saving happens.
I hope I have explained it correctly and clearly enough. It's the most complex application of Django forms I've encountered and I'm not sure what the limitations/pros/cons of each of my options is.
Has anyone encountered this situation and have a solution? Or any words to the wise?
My thanks in advance,
Nathan
I have a similar situation, I am doing something like your second and third options:
I have overridden __init__() and, after calling super, I have a loop that adds a value selector for every field (of course you could use a single custom element here)
Then override save() and after calling super I process the extra field adding all the values.
I have models like this:
class Vendor(models.Model):
title = models.CharField()
class Product(models.Model):
...
vendor = models.ForeignKey(Vendor, null=True, blank=True)
stock = models.ManyToManyField(Supplier, through='Stock')
class Stock(models.Model):
in_stock = models.BooleanField(default=True)
supplier = models.ForeignKey('catalog.Supplier', related_name='supplier_stock')
product = models.ForeignKey('catalog.Product', related_name='product_stock')
priority = models.IntegerField(default=0)
I designed models like this, because one Product can be supplied by different suppliers, and I need to know, what supplier exactly has this Product in stock.
So, in my view I want to get all results in values, to reduce number of queries and some specific logic. Also it duplicates me Product row with different Stock, by in python I group them up.
In my view I use:
Product.objects.all().values(
'id', 'title', 'vendor_code', 'vendor__title', 'price',
'product_stock__in_stock', 'stock__title', 'stock__id', 'stock__priority')
Because of INNER JOIN and null=True for Vendor related model, it returns me not all records for Product model. It just returns values where Vendor reference is set.
If I use 'vendor' instead of 'vendor__title' it returns me more results, than previous one, because in vendor field I can get {...'vendor': *id goes here*...} or {...'vendor': None...}, but I need the vendor__title value there. So any suggestions, how to achieve this?
Thanks in advance
Changed from vendor__title to product_stock__product__vendor__title helped me to fix my problem.