I am try to refer 'spot_price' of model 'Spot' in model 'Basis' in django model layer, How can I manage this?
I have designed view.py to automaticaly render the templates. so I am not able to modifty any view.py to choose data like 'models.B.objects.get().field'.
and more, str is set to indicate the date, so, in the django backstage admin, the 'spot' field display would be 'date' formate, how would be change to 'spot_price'?
model Spot
class Spot(models.Model):
date = models.DateField(primary_key=True)
spot_price = models.FloatField(blank=True, null=True)
def __str__(self):
return str(self.date) if self.date else ''
need to refer the model Spot'spot_price by date, cause date is unique but spot_price is not
class Basis(models.Model):
date = models.DateField(primary_key=True)
major_future_contract_close_price = models.FloatField(blank=True)
spot = models.OneToOneField(Spot, on_delete=models.CASCADE)
basis = models.FloatField(default=calculate_basis)
def __str__(self):
return str(self.date) if self.date else ''
def calculate_basis(self):
return abs(self.major_future_contract_close_price -
self.spot.spot_price)
I expect the Basis.query.data would to like 'date: 2019-04-25, major_future_contract_close_price: 100.0, spot: 96.5, basis: 3.5'
You can't use class method as default, because it requires self, which is not existing when you are still creating the object.
If you need to have it stored in field (database), override default save() method or use signals to modify the basis field once your object is created. Also note that you have to recalculate basis every time close_price or spot_price changes, as the value is just written in database.
Probably better solution would be to use #property so for anyone that will be using you model it will look like a field, but it will dynamically calculate value based on current data.
For example, I'm overriding save() to calculate the basis field. I set it as editable=False, which means it won't appear in forms by default (and you will not be able to see it in admin!). You can safely remove that part if you want to just look at the values.
Additionally I add basis_as_property property.
class Basis(models.Model):
date = models.DateField(primary_key=True)
major_future_contract_close_price = models.FloatField(blank=True)
spot = models.OneToOneField(Spot, on_delete=models.CASCADE)
basis = models.FloatField(editable=False, blank=True)
#property
def basis_as_property(self):
return '%s' % (self.calculate_basis())
def __str__(self):
return str(self.date) if self.date else ''
def save(self, *args, **kwargs):
if not self.basis:
self.basis = self.calculate_basis()
super(Basis, self).save(*args, **kwargs)
def calculate_basis(self):
return abs(self.major_future_contract_close_price - self.spot.spot_price)
As for Spot str repr, I don't think it's possible to change it based on where it is referenced. If you want to use spot_price, you can just use: return str(self.spot_price) if self.spot_price else str(self.date)
Related
I just started self learning Python and Django as a hobby recently, and have been trying to self develop a project which helps me in my construction business. I have developed certain functions in my project which give me the result I want, but is not the most ideal in terms of coding practises. However, I am just learning from scratch and modifying my code as I learn.
However, now I am stuck (maybe because my basic concept is only not correct?). Require help on how to proceed.
So here is my models.py
class FootingQuantity(models.Model):
member_name = models.CharField(max_length=8, unique=True)
--more fields hidden--
x_axis = models.FloatField()
y_axis = models.FloatField()
class BeamQuantity(models.Model):
work = models.ForeignKey(Work, default=1, on_delete=models.CASCADE)
member_name = models.CharField(max_length=8, unique=True)
location = models.ManyToManyField(FootingQuantity)
length = models.FloatField(blank=True)
breadth = models.FloatField()
height = models.FloatField()
-- more fields --
#property
def length_of_beam(self):
yy = self.location.all().values_list('y_axis', flat=True)
xx = self.location.all().values_list('x_axis', flat=True)
ylist = list(yy)
xlist = list(xx)
return abs(ylist[1] - ylist[0] + xlist[1] - xlist[0])
#property
def total_concrete_quantity(self):
return float(self.length) * float(self.breadth) * float(self.width)
def save(self, *args, **kwargs):
self.length = self.length_of_beam
self.total_quantity = self.total_concrete_quantity
super(BeamQuantity, self).save(*args, **kwargs)
def __float__(self):
return self.length, self.total_quantity
I want my model to accept ManytoManyRelation of 2 Footings selected and then calculate the length. The length will then multiply with height and width to give me the total quantity (There are other calculations as well but I am guessing they are all just crashing due to not getting length).
Currently, when I fill the Form or try to fill in details through the admin page and click on Save, I get a ValueError
ValueError - "<BeamQuantity: B5>" needs to have a value for field "id"
before this many-to-many relationship can be used.
I think the ManytoManyField needs to save and then the calculation needs to run. Currently both are trying to occur simultaneously and hence the error. Please help.
Exactly, you need to create your BeamQuantity instance first, before calculating the length of beans. Leave those fields empty for a second.
To do that, I recommend you to let all the fields that require the many to many relationship to have a value, with blank=True and null=True.
So, I would rewrite the save method like this:
def save(self, *args, **kwargs):
if self.id: # it means the object was already created, so has an id.
self.length = self.length_of_beam
self.total_quantity = self.total_concrete_quantity
super(BeamQuantity, self).save(*args, **kwargs)
Then when you want to create a BeanQuantity, do:
bean_quantity = BeanQuantity.objects.create(**fields_you_want_here)
bean_quantity.save()
The second line will run the code in the save method, because now the object has an id.
In my model I need an ID field that is different from the default ID given by Django. I need my IDs in the following format: [year][ascending number]
Example: 2021001,2021002,2021003
The IDs shall not be editable but model entries shall take the year for the ID from a DateTimeField in the model. I am unsure if I use a normal Django ID and also create some kind of additional ID for the model or if I replace the normal Django ID with a Custom ID.
This problem is pretty similar to one I had solved for a previous project of mine. What I had done for this was to simply use the default id for the primary key, while using some extra fields to make the composite identifier needed.
To ensure the uniqueness and the restarting of the count I had made a model which would (only by the logic, no actual constraints) only have one row in it. Whenever a new instance of the model which needs this identifier would be created this row would be updated in a transaction and it's stored value would be used.
The implementation of it is as follows:
from django.db import models, transaction
import datetime
class TokenCounter(models.Model):
counter = models.IntegerField(default=0)
last_update = models.DateField(auto_now=True)
#classmethod
def get_new_token(cls):
with transaction.atomic():
token_counter = cls.objects.select_for_update().first()
if token_counter is None:
token_counter = cls.objects.create()
if token_counter.last_update.year != datetime.date.today().year:
token_counter.counter = 0
token_counter.counter += 1
token_counter.save()
return_value = token_counter.counter
return return_value
def save(self, *args, **kwargs):
if self.pk:
self.__class__.objects.exclude(pk=self.pk).delete()
super().save(*args, **kwargs)
Next suppose you need to use this in some other model:
class YourModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
yearly_token = models.IntegerField(default=TokenCounter.get_new_token)
#property
def token_number(self):
return '{}{}'.format(self.created_at.year, str(self.yearly_token).zfill(4))
#classmethod
def get_from_token(cls, token):
year = int(token[:4])
yearly_token = int(token[4:])
try:
obj = cls.objects.get(created_at__year=year, yearly_token=yearly_token)
except cls.DoesNotExist:
obj = None
return obj
Note: This might not be very refined as the code was written when I was very inexperienced, and there may be many areas where it can be refined. For example you can add a unique_for_year in the yearly_token field so:
yearly_token = models.IntegerField(default=TokenCounter.get_new_token, unique_for_year='created_at')
I have some code in which I have defined some Python models.
For example:
class Store(object):
'''
The store object... Should it be a Django model?
'''
def __init__(self, *args, **kwargs):
self.name = kwargs['name']
self.id = kwargs['id']
def __str__(self, *args, **kwargs):
return self.name
kittenstore = Store(name='kittenstore', id=1)
dogstore = Store(name='dogstore', id=2)
stores_list = [kittenstore, dogstore]
class Stores(object):
def __init__(self, *args, **kwargs):
self.stores = stores_list
def get_choices(self):
store_choices = ()
for store in self.stores:
store_choices+= ((store.id, store.name),)
return store_choices
I'd like to refer to those stores from another Django model.
I could use a models.Integerfield with choices=get_choices(), but when I filter in a related models, then I always get the id back and I need to find the correlating store if I want to use it in my code.
Is there not a (python) trick to get back the Store object instead of the id?
Maybe with a custom Field?
"Referring to these stores from another model" implies that there should be some kind of (logical) relationship between those models. This is usually depicted by connecting the models with a foreign key.
In other words, you can try something like this:
from django.db import models
class Store(models.Model):
name = models.CharField(max_length=50)
id = models.CharField(max_length=5)
def __str__(self):
return 'Store is named: %s' % self.name
class Stores(models.Model):
store = models.ForeignKey(Store, on_delete=models.CASCADE)
def __str__(self):
return "Stores is at %s store" % (self.store)
Create your store and save it:
kittenstore = Store(name='kittenstore', id='1')
kittenstore.save()
Instantiate your Stores:
st = Stores(id=None, store=kittenstore)
st.save()
And now you can access your store object like this:
st.kittenstore
The relationship I used in this example is a Many-to-one relationship. You should decide on the relationship that best suits your needs by taking a look at the Django documentation regarding model relationship.
***EDIT***If you mean associate them via the model's attributes then you can assign a model attribute to an instance of a class. For a related example on the matter, take a look at this.
I have a problem to set a default field value. What I want to do?I want that price in class Packages be a default value of priceNoTax in class Bill. As you can see, all three classes are
logically connected.Example: Account 1 has a package with id 1. Price of this package is 100. Default value of priceNoTax for Account 1 is 100.How to do that? I am relative new at this, so I need help.
models.py
class Packages(models.Model):
#other fields
price = models.IntegerField(validators=[MinValueValidator(1)], verbose_name="Price of package")
class Account(models.Model):
startDate = models.DateField(verbose_name="Start date")
finishDate = models.DateField(verbose_name="Finish date")
idPackage = models.ForeignKey(Packages, on_delete=models.CASCADE, verbose_name="Package")
class Bill(models.Model):
date = models.DateField(default=datetime.now())
tax = models.FloatField(default=0.20)
priceNoTax = models.IntegerField()
priceTax = models.FloatField(default=priceNoTax+(priceNoTax*tax))
idAccount = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="Account")
def __str__(self):
return self.date
Thanks a lot!!!
perhaps add this to your Bill class?
def save(self, *args, **kwargs):
if self.priceNoTax is None:
self.priceNoTax = self.idAccount.idPackage.price
super(Bill, self).save(*args, **kwargs)
Why do you need it to be a field? Do you see a reason where someone would want to change the total price without changing the price and tax to the corresponding values? If it doesn't really need to be a field, you can just make it a method.
class Bill(models.Model):
date = models.DateField(default=datetime.now())
tax = models.FloatField(default=0.20)
priceNoTax = models.IntegerField()
idAccount = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="Account")
def priceTax(self):
return self.priceNoTax + (self.priceNoTax*self.tax)
def __str__(self):
return self.date
You can still use it the same way in templates with {{ bill.priceTax }}. In code you would need to use bill.priceTax().
This way, the price with tax should stay up-to-date no matter how the tax or price without tax changes.
You can also use #property decorator to avoid having to call it as a function in code.
#property
def priceTax(self):
return self.priceNoTax + (self.priceNoTax*self.tax)
For more see https://docs.djangoproject.com/en/2.0/topics/db/models/#model-methods
#kichik answered how to display the default value using template tag, and I recommend to implement auto-calculation by javascript.
Of course, you will have to validate user input or implement save() method as #Chris Curvey said.
I want to generate an unique string in my django1.6 application. For that I want to concatenate the primary key (autofield) with the string. My code is
class Complaints(models.Model):
gid = models.AutoField(primary_key=True)
complaint_no = models.CharField(max_length=50)
status = models.IntegerField(default=0,blank=False,null=False)
def __str__(self):
return ('%s %s' % (self.gid, self.complaint_no))
def save(self, force_insert=False, force_update=False):
self.complaint_no = '%s%d' % (self.complaint_no,self.gid)
super(Complaints, self).save(force_insert, force_update)
class Meta:
db_table = 'complaints'
verbose_name = "complaints"
But I got the error message as
TypeError at /callcentre/register/
%d format: a number is required, not NoneType
Please help me to solve this error!
Create field id as charfield e.g.
string_id = models.CharField(max_length=100, default='file')
Make a function that make string id when the model is saved.
def save(self):
new_id = self.id
self.string_id = str(new_id) + '_dot'
super(YourModelName, self).save()
You need to call super() first.
As you can read in the documentation:
There’s no way to tell what the value of an ID will be before you call save(), because that value is calculated by your database, not by Django.
When you call super().save(), the database engine calculates the id (or gid in your case, but please reconsider using the provided id) and thus allows you to use it. Before that it is None.
You may check this topic as well.