Problem :
So, I have been trying to make an object of the model Trade with an initial value to the identifier from my custom function gen_rand_string().
But the problem is when, I am initialising it.
If I remove the initializer and set the UUIDField to NULL, it works out to be fine.
This is the error, I am getting :
TypeError at /admin/account/trade/add/
int() argument must be a string or a number, not 'Trade'
My Trade class :
class Trade(models.Model):
NEW_TRADE = 'N'
CANCELLED_TRADE = 'C'
PENDING_TRADE = 'P'
STATUS_OF_TRADE = (
(NEW_TRADE, "New"),
(CANCELLED_TRADE, "Cancelled"),
(PENDING_TRADE, "Pending")
)
TYPE_BUY = 'B'
TYPE_SELL = 'S'
TYPE_OF_TRADE = (
(TYPE_BUY, "Buy"),
(TYPE_SELL, "Sell")
)
user = models.OneToOneField('UserProfile', related_name='trades')
identifier = models.UUIDField(null=False, editable=False)
created_at = models.DateTimeField(auto_now_add=True, editable=False)
finished_at = models.DateTimeField(auto_now_add=True)
amount = models.DecimalField(max_digits=19, decimal_places=10, null=False)
type = models.CharField(max_length=2, choices=TYPE_OF_TRADE, null=False)
status = models.CharField(max_length=2, choices=STATUS_OF_TRADE, default=PENDING_TRADE, null=False)
def __init__(self, *args, **kwargs):
self.identifier = gen_rand_string()
super(Trade, self).__init__(self, *args, **kwargs)
class Meta:
ordering = ('created_at',)
def __unicode__(self):
return self.identifier
def __str__(self):
return self.identifier
My gen_rand_string() :
def gen_rand_string(purpose=''):
if purpose:
return purpose + '_' + get_random_string(length=64 - len(purpose))
else:
return get_random_string(length=64)
Suggestions :
I am making a random string for each trade in not a better way, would someone suggest somewhat better option, or something they would have it in their place.
I am using python 2.7 which is the reason of my initialisation of the object in a different way
Thanks.
You are discouraged from overriding __init__ for Django models. You use your gen_rand_string as the field's default:
identifier = models.UUIDField(null=False, editable=False, default=gen_rand_string)
However you probably don't need to define your own gen_rand_string method - just use uuid.uuid4, as in docs for UUIDField.
identifier = models.UUIDField(null=False, editable=False, default=uuid.uuid4)
problem is with your init function syntax.
def __init__(self, *args, **kwargs):
super(Trade, self).__init__(self, *args, **kwargs)
self.identifier = gen_rand_string()
Related
I saw this answer but there is no specific answer yet. I want to create custom id that starts with letter. When a new record comes into database I want to change the id to A00001, .... A00002, .... A00010, ...A10000 etc. The id will be always in range 99999- 00001 so how can I do that?
my model is simple:
class Custom(models.Model):
id = models.AutoField(primary_key=True, editable=False)
The AutoField field is a kind of IntegerField field, so you can't use PKs as A00001 .
So, the possible way to achieve the requirement is to change the AutoField to CharField.
Technically you can use "String PK Field" But, you should be aware of the problems/performance issues if you are going to use that.
Here I found one nice SO post that explains the same - Strings as Primary Keys in SQL Database========================================================================
If you still really wish to migrate to String PKs, read the following
First you need to use the CharField instead of AutoField and override the save() method of model
from django.db.models import Max
class Custom(models.Model):
id = models.CharField(primary_key=True, editable=False, max_length=10)
name = models.CharField(max_length=100)
def save(self, **kwargs):
if not self.id:
max = Custom.objects.aggregate(id_max=Max('id'))['id_max']
self.id = "{}{:05d}".format('A', max if max is not None else 1)
super().save(*kwargs)
string as Primary Key not good idea if you plan to do references to the table, so i recommend you to add a property, for example:
class Custom(models.Model):
id = models.AutoField(primary_key=True, editable=False)
#property
def sid(self):
return "A%05d" % self.id
and to do queries you can do processing the input values, for example:
s_input = "A%05d" % 231 # 'A00231'
number = s_input[1:] # '00231'
input_id = int(number) # 231
I also have another way, That i use in my django project. Here are some code
def ids():
no = Employee.objects.count()
if no == None:
return 1
else:
return no + 1
emp_id = models.IntegerField(('Code'), default=ids, unique=True, editable=False)
id = models.CharField(primary_key=True, editable=False, max_length=30)
def save(self, **kwargs):
if not self.id:
self.id = "{}{:08d}".format('ABC', self.emp_id)
super().save(*kwargs)
It's better to create a new field for the custom id in the models and the process in the backend. You can set that as primary_key with unique=True and editable=False:
class Custom(models.Model):
id = models.Autofield(primary_key=True, editable=False, max_length=10)
uid= models.CharField(max_length=100, unique=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.set_uid() # calling the set_uid function
def set_uid(self):
if not self.uid: # if uid of the instance is blank
uid = "CUS" + str(self.id + (10 ** 5)) # generating the uid
customer= Custom.objects.get(id=self.id) # getting the instance
customer.uid = uid # allocating the value
customer.save() # saving the instance
def __str__(self):
return self.uid
Can also merge the set_uid() inside the save() where the function is called:
class Custom(models.Model):
id = models.Autofield(primary_key=True, editable=False, max_length=10)
uid= models.CharField(max_length=100, unique=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not self.uid: # if uid of the instance is blank
self.uid = "CUS" + str(self.id + (10 ** 5)) # generating the uid and allocating the value
self.save() # saving the instance
def __str__(self):
return self.uid
I tried to use answer of #JPG, but it has a bug.
The bug is becasue it can't auto increment.
I fixed the bug, and this my resultant code:
def save(self, **kwargs):
if not self.id:
max = YourModel.objects.aggregate(
id_max=models.Max('id'))['id_max']
if max is not None:
max += 1
else:
max = 100
self.id = "{:08d}".format(
max) # id from 100 to start
super().save(*kwargs)
Model Device:
class Device(models.Model):
device_code = models.CharField(max_length=64,unique=True)
is_enabled = models.BooleanField(default=False)
def __unicode__(self):
return u'%s: %s' % (self.device_code, 'ENABLED' if self.is_enabled else 'DISABLED')
Model AttributeValues:
class AttributeValue(models.Model):
attribute = models.ForeignKey(Attribute)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Meta:
abstract = True
unique_together = (
('attribute', 'content_type','object_id'),
)
index_together = (
('content_type','object_id'),
)
#property
def formatted(self):
"""
PLEASE SELECT RELATED ATTRIBUTE BEFORE USING THIS FUNCTION
"""
return self.attribute.format % self.value
def save(self,*args,**kwargs):
if hasattr(self.content_object,'invalidate_cache') and callable(self.content_object.invalidate_cache):
self.content_object.invalidate_cache()
super(AttributeValue,self).save(*args, **kwargs)
def __unicode__(self):
return u'%s %s' % (self.attribute.name, self.value)
class NumericAttributeValue(AttributeValue):
value = models.DecimalField(max_digits=12,decimal_places=4)
class LongTextAttributeValue(AttributeValue):
value = models.TextField()
class ShortTextAttributeValue(AttributeValue):
value = models.CharField(max_length=255)
class FileAttributeValue(AttributeValue):
attribute_file = models.FileField(upload_to="attribute_imgs")
Model Attribute:
ATTRIBUTE_TYPE_CHOICES = (
('n','Numeric'),
('s','Short Text (255)'),
('m','Long Text')
)
class Attribute(models.Model):
name = models.CharField(max_length=255)
code = models.CharField(max_length=64,unique=True)
attribute_type = models.CharField(max_length=1,choices=ATTRIBUTE_TYPE_CHOICES)
sorting_order = models.PositiveIntegerField(default=0)
show = models.BooleanField(default=False)
format = models.CharField(max_length=64,default='%s')
class Meta:
ordering = ['sorting_order','name']
def __unicode__(self):
return self.name
In my device editing (adding) page, it needs to be able to create or select an attribute, then create (or edit / delete) an attribute value (could be a numeric value, long text value, short text value or a file) associated to this attribute, and the current (or new) device. How would you create a django formset for this kind of scenario?
I had to solve a similar problem and django-polymorphic worked for me.
If you define an abstract model as the parent, then it allows you to select any child models that the parent is based on in the Django admin interface (when selecting a foreign-key for example).
You will have to make some changes in your model & admin to get it working (for eg; you won't need GenericForeignKey).
https://django-polymorphic.readthedocs.org/en/latest/
I'm playing around in Django, and wondering if there is a way to loop through instances of two different models I have created?
/ models.py:
class Tran(models.Model):
name = models.CharField(max_length=300)
description = models.CharField(max_length=2000)
type = models.ForeignKey(TransactionType)
def __str__(self):
return self.name
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
What I want to do:
Look through each of the Tran instances and create a default value for the links/displays in the DocLink table instead of doing it manually.
Is there anyway I can be pointed in the right direction?
If you want to set links/displays default value in DocLink instance based on trann field you can override model's save method.
For example following code shows how to set t_link if it doesn't have a value:
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
def save(self, *args, **kwargs):
if not self.t_link:
pass # TODO set self.t_link based on trann
super(DocLink, self).save(*args, **kwargs)
Also you can change model's trann field to:
trann = models.ForeignKey(Transaction, related_name="doclinks")
And then access to all DocLinks of a Tran with:
# t is an instance of Tran class
t.doclinks.all()
So you can loop through this list and do what you want.
edit: I completely rewrote the question as the original one didn't clearly explain my question
I want to run a function which is specific to each particular model instance.
Ideally I want something like this:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = models.FunctionField() #stores a function specific to this instance
x = MyModel(data='originalx', perform_unique_action=func_for_x)
x.perform_unique_action() #will do whatever is specified for instance x
y = MyModel(data='originaly', perform_unique_action=func_for_y)
y.perform_unique_action() #will do whatever is specified for instance y
However there is no datatype FunctionField. Normally this would be solvable with inheritance, and creating subclasses of MyModel, maybe like this:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = default_function
class MyModelX(MyModel):
perform_unique_action = function_X
class MyModelY(MyModel):
perform_unique_action = function_Y
x = MyModelX(data='originalx')
x.perform_unique_action() #will do whatever is specified for instance x
y = MyModelY(data='originaly')
y.perform_unique_action() #will do whatever is specified for instance y
Unfortunately, I don't think I can use inheritance because I am trying to access the function this way:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = default_function
class SecondModel(models.Model):
other_data = models.IntegerField()
mymodel = models.ForeignKey(MyModel)
secondmodel = SecondModel.objects.get(other_data=3)
secondmodel.mymodel.perform_unique_action()
The problem seems to be that I don't know what type the foreign key is going to be in SecondModel if I override the perform_unique_action in subclasses.
Can I access MyModel from SecondModel as a foreign key and still have a unique function for each instance of MyModel?
This works for me. I haven't tested it, but you should be able to create another class and override their methods and it'll work. Check the class Meta line, it'll treat it as an abstract class. Here's an example of my actual classes that I'm working on right now.
EDIT: Added VoteComment class and tested it. It works as expected!
class Vote(models.Model):
VOTE_ENUM = (
(VoteEnum.DOWN_VOTE, VoteEnum.toString(VoteEnum.DOWN_VOTE)),
(VoteEnum.NONE, VoteEnum.toString(VoteEnum.NONE)),
(VoteEnum.UP_VOTE, VoteEnum.toString(VoteEnum.UP_VOTE)),
)
question = models.ForeignKey(Question, null=False, editable=False, blank=False)
voter = models.ForeignKey(User, blank=False, null=False, editable=False)
vote_type = models.SmallIntegerField(default=0, null=False, blank=False, choices=VOTE_ENUM)
class Meta:
abstract = True
def is_upvote(self):
return self.vote_type > 0
def is_downvote(self):
return self.vote_type < 0
class VoteAnswer(Vote):
answer = models.ForeignKey(Answer, null=False, editable=False, blank=False)
class Meta:
unique_together = (("voter", "answer"),) # to prevent user from voting on the same question/answer/comment again
def __unicode__(self):
vote_type = "UP" if vote_type > 0 else ("DOWN" if vote_type < 0 else "NONE")
return u"{0}: [{1}] {2}".format(user.username, vote_type, answer.text[:32])
def is_upvote(self):
return "FOO! "+str(super(VoteAnswer, self).is_upvote())
class VoteComment(Vote):
comment = models.ForeignKey(Comment, null=False, editable=False, blank=False)
class Meta:
unique_together = (("voter", "comment"),) # to prevent user from voting on the same question/answer/comment again
def __unicode__(self):
vote_type = "UP" if vote_type > 0 else ("DOWN" if vote_type < 0 else "NONE")
return u"{0}: [{1}] {2}".format(user.username, vote_type, comment.text[:32])
def is_upvote(self):
return "BAR!"
I came up with two ways of having a specific function defined for each object. One was using marshal to create bytecode which can be stored in the database (not a good way), and the other was by storing a reference to the function to be run, as suggested by Randall. Here is my solution using a stored reference:
class MyModel(models.Model):
data = models.CharField(max_length=100)
action_module = models.CharField(max_length=100)
action_function = models.CharField(max_length=100)
class SecondModel(models.Model):
other_data = models.IntegerField()
mymodel = models.ForeignKey(MyModel)
secondmodel_obj = SecondModel.objects.get(other_data=3)
#The goal is to run a function specific to the instance
#of MyModel referred to in secondmodel_obj
module_name = secondmodel_obj.mymodel.action_module
func_name = secondmodel_obj.mymodel.action_function
module = __import__(module_name)
func = vars(module)[func_name]
func()
Thanks to everyone who replied, I couldn't have got to this answer if it weren't for your help.
You could achive some similar behavior overriding the save method. And providing special callbacks to your instances.
Something like:
def default_function(instance):
#do something with the model instance
class ParentModel(model.Model):
data = models.CharField()
callback_function = default_function
def save(self, *args, **kwargs):
if hasattr(self, 'callback_function'):
self.callback_function(self)
super(ParentModel, self).save(*args, **kwargs)
class ChildModel():
different_data = models.CharField()
callback_function = other_fun_specific_to_this_model
instance = ChildModel()
#Specific function to this particular instance
instance.callback_function = lambda inst: print inst.different_data
instance.save()
You can write endpoints on your server and limit their access to just your self. Then store in each model instance corresponding url. For example:
views.py
def funx_x(request):
pass
def func_y(request):
pass
models.py:
class MyModel(models.Model):
data = models.CharField(max_length=100)
perform_unique_action = models.URLField()
and then:
x = MyModel(data='originalx', perform_unique_action='http://localhost/funx_x')
requests.post(x.perform_unique_action)
i dont know whether i understand u correct or not. but you can check out this example here.
Example:
A string representing an attribute on the model. This behaves almost the same as the callable, but self in this context is the model instance. Here's a full model example:
class Person(models.Model):
name = models.CharField(max_length=50)
birthday = models.DateField()
def decade_born_in(self):
return self.birthday.strftime('%Y')[:3] + "0's"
decade_born_in.short_description = 'Birth decade'
class PersonAdmin(admin.ModelAdmin):
list_display = ('name', 'decade_born_in')
I am trying to set a default value for attribute threshold in this code, the threshold should be the current level*50 and this is the model
class Level (models.Model):
name = models.CharField(max_length=20,null=True, blank=True)
description = models.CharField(max_length=20, null=True, blank=True)
number = models.IntegerField(null=True, blank=True)
threshold = models.IntegerField(null=True, blank=True,default=50*number,editable=False)
i get an error unsupported operand types for * : 'int' and 'IntegerField'
You best best is to do such calculation while saving the object. So override Model.save
or a better generic way would be to write a custom field and override pre_save
class DependentIntegerField(models.IntegerField):
def pre_save(self, model_instance, add):
if not add: # set the default only while adding model
return super(self, DependentIntegerField).pre_save(model_instance, add)
return model_instance.number*50
You can further enhance it and make DependentIntegerField generic so that you can pass callable to it and do any calculation, and you can do further enhancements like checking if user has set the value or not before using default value, and to make it more generic so that you can make any Field as dependent field by passing the field class to a factory function. e.g.
from django.db import models
class_map = {}
def depends_field_pre_save(self, model_instance, add):
"""
if default is not callable or it is not a model add, lets skip our hook
"""
if not add or not callable(self.default):
super(self.__class__, self).__init__(self,*args, **kwargs)
value = self.default(model_instance)
setattr(model_instance, self.attname, value)
return value
def FieldDepends(field_class):
"""
return a dervied class from field_class which supports dependent default
"""
if field_class in class_map:
# we already created this class so return that
return class_map[field_class]
new_class = type('Depends'+field_class.__name__, (field_class,), {'pre_save':depends_field_pre_save })
class_map[field_class] = new_class
return new_class
and use it like this
class DependentModel(models.Model):
def threshold_default(model_instance=None):
if model_instance is None:
return 10
return model_instance.number*10
number = models.IntegerField(null=True, blank=True, default=10)
threshold = FieldDepends(models.IntegerField)(null=True, blank=True, default=threshold_default,editable=False)
I have created a small django project djangodepends on bitbucket with test cases
You can override save method to calculation.
https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods
class Level (models.Model):
name = models.CharField(max_length=20,null=True, blank=True)
description = models.CharField(max_length=20, null=True, blank=True)
number = models.IntegerField(null=True, blank=True)
threshold = models.IntegerField(null=True, blank=True ,editable=False)
def save(self, *args, **kwargs):
try:
self.threshold = self.number * 50
except TypeError:
pass
super(Level, self).save(*args, **kwargs) # Call the "real" save() method.