Django REST Framework: custom serializer variables - django

I am using a model serializer (many=True) in Django Rest Framework where I want to return booking_color and text_color properties in JSON in order to display a booking instance on a calendar plugin. Both properties depend on job_type and job_status variables that are calculated (using foreign keys, etc.). I want to run a calculation for those variables when a particular instance gets initialized (in the init method) so that both calculated values then become available for both method fields (booking_color and text_color). The init method, however, passes entire queryset as 'inst' and so I can't do instance-specific calculations. What would be the best way around this? I have previously ran those calculations in the first method in the list ('get_booking_color' in this case) and it works, but it isn't very elegant and I am not sure if I am supposed to do it.
class CalendarView(serializers.ModelSerializer):
booking_color = serializers.SerializerMethodField()
text_color = serializers.SerializerMethodField()
def __init__(self, inst):
self.job_type = [complicated calculation that depends on inst values]
self.invoice_status = [complicated calculation that depends on inst values]
def get_booking_color(self, inst):
if self.invoice_status == 1:
if self.job_type == 1:
return "#000000"
elif self.job_type == 2:
return "#f1c40f"
elif self.job_type == 3:
return "#FFFFF"
else:
return '#faase4'
def get_text_color(self, inst):
if self.invoice_status == 2:
if self.job_type == 1:
return "#BBFF33"
elif self.job_type == 2:
return "#272844"
elif self.job_type == 3:
return "#2c0716"
else:
return '#FFFFF'

I believe you need to modify your __init__() call to this:
def __init__(self, instance=None, data=empty, **kwargs):
self.job_type = [complicated calculation that depends on inst values]
self.invoice_status = [complicated calculation that depends on inst values]
super(CalendarViewSerializer, self).__init__(**kwargs)
I'd also recommend renaming your class to CalendarViewSerializer so it is not confused with anythign else.
You may also be able to move around overriding the __init__() call by passing in those calculations via the context - then working with them from there...e.g.,
serializer = CalendarViewSerializer(data=request.data, context={'job_type': ..., 'invoice_status': ...})
class CalendarViewSerializer(serializers.ModelSerializer):
booking_color = serializers.SerializerMethodField()
text_color = serializers.SerializerMethodField()
def get_booking_color(self, inst):
if self.context['invoice_status'] == 1:
if self.context['job_type'] == 1:
return "#000"
elif self.context['job_type'] == 2:
return "#f1c40f"
elif self.context['job_type'] == 3:
return "#fff"
else:
return '#faase4'
def get_text_color(self, inst):
if self.context['invoice_status'] == 2:
if self.context['job_type'] == 1:
return "#bbff33"
elif self.context['job_type'] == 2:
return "#272844"
elif self.context['job_type'] == 3:
return "#2c0716"
else:
return '#fff'
As an extra bonus, I believe you could use some sort of dict()/{} object to return the hex codes from a key lookup, rather than the if elseif elseif else statements.

Related

implement simplification rule for special functions

I am defining two custom functions in Sympy, called phi and Phi. I know that Phi(x)+Phi(-x) == 1. How do I provide Sympy with this simplification rule? Can I specify this in my class definition?
Here is what I've done so far:
from sympy import Function
class phi(Function):
nargs = 1
def fdiff(self, argindex=1):
if argindex == 1:
return -1*self.args[0]*phi(self.args[0])
else:
raise ArgumentIndexError(self, argindex)
#classmethod
def eval(cls, arg):
# The function is even, so try to pull out factors of -1
if arg.could_extract_minus_sign():
return cls(-arg)
class Phi(Function):
nargs = 1
def fdiff(self, argindex=1):
if argindex == 1:
return phi(self.args[0])
else:
raise ArgumentIndexError(self, argindex)
For the curious, phi and Phi represent the Gaussian PDF and CDF, respectively. These are implemented in sympy.stats. But, in my case, it's easier to interpret results in terms of phi and Phi.
Based upon the comment by Stelios, Phi(x) should return 1-Phi(-x) if x is negative. Therefore, I modified Phi as follows:
class Phi(Function):
nargs = 1
def fdiff(self, argindex=1):
if argindex == 1:
return phi(self.args[0])
else:
raise ArgumentIndexError(self, argindex)
#classmethod
def eval(cls, arg):
# Phi(x) + Phi(-x) == 1
if arg.could_extract_minus_sign():
return 1-cls(-arg)

Extensible Hashing with unique keys

I have a database that consists of tuples like so
'The Abyss,1989,LaserDisc,Science Fiction,James Cameron,James Cameron,USA,20th Century Fox,$0.00'
I want to concatenate the movie title with the year to make the unique key for each bucket. But unsure how to... I think it would be beneficial to use extensible hashing for this,
I would like to be able to search by movies being DVD or VHS as well as searching through and finding movies by year. I would consist buckets of years in a decade increments and types of movie (DVD, VHS)
Right now I just have a simple add, remove, and get functionality
class HTable(object):
def __init__(self, table = [], maximum = 100):
#table = dict, maximum = maximum amount of elements.
assert type(table) == dict
self.table = table
self.max = maximum
def lookup(self, data):
#Lookup a value in our table.
if type(data) == int or type(data) == long:
try:
if self.table[data % self.max] != None:
return (data % self.max, self.table[data % self.max])
else:
return None
except:
return None
else:
try:
obj1 = self.string2int(data) % self.max
obj2 = self.table[self.string2int(data) % self.max]
if obj2 != None:
return (obj1, obj2)
else:
return None
except:
return None
def append(self, data):
#data = int, string, object, etc. No duplicates allowed.
assert len(self.table) < self.max
if type(data) == int or type(data) == long:
original = data
if data >= self.max:
raise IOError, "Value to large to append into hash table. Max limit reached."
else:
original = data
data = self.string2int(data)
index = data % self.max
if int(index) >= self.max:
raise IOError, "Data: %s, exceeded your maximum limit of %s, with the size of %s." %(str(original), str(self.max), str(index))
try:
if type(original) == int or type(original) == long:
self.table[data % self.max] = data
else:
self.table[data % self.max] = original
return self.table
except:
if len(self.table) < data % self.max:
while len(self.table) < data % self.max:
self.table.append(None)
if type(original) == int:
self.table.insert(data % self.max, data)
else:
self.table.insert(data % self.max, str(original))
return self.table
def string2int(self, STRING):
#Convert a string into a 'hash' integer.
return sum([ord(j) for j in STRING])
def isEmpty(self):
#Return True if empty, false otherwise.
if len(self.table) == 0:
return True
else:
return False
def isFull(self):
#Returns TRUE if full, false otherwise.
if len(self.table) == self.max:
return True
else:
return False
def remove(self, key):
#Remove the data located at the given index/key. Key can be a index key(integer), or the data itself. For example: self.remove(key = 'value') or self.remove(key = 10).
try:
self.table.pop(int(key))
return 1
except:
try:
self.table.remove(key)
return 1
except:
return False
def get(self, key):
#Get the data in our HASH Table, using the given index(key).
try:
return self.table[int(key)]
except:
return None
def output(self):
#Return our current HASH Table.
return self.table

What is the efficient way to sort custom fields in django models?

I load all the leads, iterate the queryset and populate the custom fields.
The custom fields are dependent on other model.
Then I sort the leads by these custom fields and show the result.
This method is very slow.
How can I optimize and increase speed?
The models are as follows
Lead Model
class Lead(LeadModel):
def most_recent_mailing_date(self):
""" Return the most recent mailing date """
mailingHistories = self.mailinghistory_set.all()
if len(mailingHistories) != 0:
today = datetime.date.today()
mostRecentHistory = None
diff = -1
for mailingHistory in mailingHistories:
if mailingHistory.mailing_date < today and (diff == -1 or (today - mailingHistory.mailing_date) < diff):
mostRecentHistory = mailingHistory
diff = today - mostRecentHistory.mailing_date
if mostRecentHistory is None:
return "No Mailing History"
else:
return mostRecentHistory.mailing_date
else:
return "No Mailing History"
def next_mailing_date(self):
""" Return the next mailing date """
mailingHistories = self.mailinghistory_set.all()
if len(mailingHistories) != 0:
today = datetime.date.today()
nextHistory = None
diff = -1
for mailingHistory in mailingHistories:
if mailingHistory.mailing_date > today and (diff == -1 or (mailingHistory.mailing_date - today) < diff):
nextHistory = mailingHistory
diff = mailingHistory.mailing_date - today
if nextHistory is None:
return "No Future Mailings"
else:
return nextHistory.mailing_date
else:
return "No Future Mailings"
Mailing History Model
class MailingHistory(models.Model):
lead = models.ForeignKey(Lead)
returned_envelope = models.BooleanField()
mailing_date = models.DateField(blank=True, null=True)
Leads to list function
def leads_to_list(queryset):
holder = []
for item in queryset:
item_dict = item.__dict__
recent_mailing_date = item.most_recent_mailing_date()
next_mailing_date = item.next_mailing_date()
if not type(recent_mailing_date) == datetime.date:
recent_mailing_date_key = NONE_DATE
else:
recent_mailing_date_key = recent_mailing_date
if not type(next_mailing_date) == datetime.date:
next_mailing_date_key = NONE_DATE
else:
next_mailing_date_key = next_mailing_date
item_dict['recent_mailing_date'] = recent_mailing_date
item_dict['recent_mailing_date_key'] = recent_mailing_date_key
item_dict['next_mailing_date'] = next_mailing_date
item_dict['next_mailing_date_key'] = next_mailing_date_key
if '_state' in item_dict:
del item_dict['_state']
holder.append(item_dict)
return holder
Sorting Logic
# Code to be optimized #
leads = Lead.objects.all()
leads = queryset_to_list(leads) # Important for serialization. json.dumps
sort_key = 'recent_mailing_date_key'
sort_reverse = True
leads = sorted(leads,key=itemgetter(sort_key),reverse = sort_reverse)
return json.dumps(leads)
You can use filter, limit and order by in query.
Be careful with len of queryset. The function len counts objects of a list. It is more efficient to use count (function that runs a query to count).
I hope I've helped.
class Lead(LeadModel):
def most_recent_mailing_date(self):
""" Return the most recent mailing date """
today = datetime.date.today()
mailingHistories = self.mailinghistory_set.filter(mailing_date__lt=today).order_by('-mailing_date', '-id')[:1]
if len(mailingHistories) != 0:
return mostRecentHistory[0].mailing_date
else:
return "No Mailing History"
def next_mailing_date(self):
""" Return the next mailing date """
today = datetime.date.today()
mailingHistories = self.mailinghistory_set.filter(mailing_date__gt=today).order_by('mailing_date', 'id')[:1]
if len(mailingHistories) != 0:
return mostRecentHistory[0].mailing_date
else:
return "No Future Mailings"

Questions about python inheritance and argument lists

First off I am getting this error
File "E:\New folder (7)\maingame.py", line 64, in play print self.introduction AttributeError: 'game' object has no attribute 'introduction'
I am not to sure as to what it means because I am pulling the self.introduction from the previous class..
I am also getting an
File "E:\New folder (7)\maingame.py", line 96, in <module>
game.play()
TypeError: play() takes exactly 2 arguments (1 given)
error, but can't for the life of me find what argument it is looking for, I simply want it to work.
from random import random
class place(object):
def __init__(self, title, description, events):
self.title = title
self.description = description
self.events = events
class event(object):
def __init__(self, probability, message, healthChange):
self.probability = probability
self.message = message
self.healthChange = healthChange
def process(self):
if random() < self.probability:
print self.message
return self.healthChange
return 0
class textadventure():
def __init__(self):
super(textadventure, self).__init__()
self.introduction = """
Welcome player, you are a lone traveler in space whom has set out to find glories beyond measure.
Unfortunately for you the dread pirate Roberts has attacked. You must defeat him.
"""
commandDeck = place('Command Deck', "You are now in the command center, here you can drive the ship and fire its weapons.",(
event(0.7, "The pirate ship fires at you! You take damage to your engines!", -10),
event(0.2, "One of the pirates manages to beam onto your ship! He shoots you before beaming away!",0),
))
engineRoom = place('Engine Room', "You are now in the main engine room here you can repair damage to the ship",(
event(0.7, "The pirate ship fires at you! You take damage to your engines!", -10),
))
restQuarters = place('Resting Quarters', "Here you can take a rest and heal your self",(
event(1.0, 'You are able to patch up your wounds and get back to the battle',0),
event(0.5, "The pirate ship fires at you! You take damage to your engines!", -10),
))
commandDeck.transitions = (engineRoom, restQuarters),
engineRoom.transitions = (commandDeck, restQuarters),
restQuarters.transitions = (commandDeck, engineRoom),
self.location = commandDeck
pirateHp = 50
class game(object, textadventure):
def __init__(self):
super(game, self).__init__()
self.health = 100
def location(self):
if self.location == commandDeck:
choice = raw_input('would you like to fire on the enemy ship?')
if choice == 'yes':
print 'You have hit the pirates!'
pirateHp -= 10
else: choice == 'no'
elif self.location == engineRoom:
choice = raw_input('Would you like to repair the engines?')
if choice == "yes":
event(1, "You repair what you can of the engines.", 10)
def __init__(self):
self.health = 100
def play(self, textadventure):
print textadventure.introduction
while True:
print (self.location.description)
for event in self.location.events:
self.health += event.process()
if self.health <= 0:
print ("Your ship has been destroyed!")
pause
exit(1)
print ('Your ships health is at %d percent' % self.health)
self._transition()
def _transition(self):
transitions = self.location.transitions
print ('you can go to: ')
for (index, transition) in enumerate(transitions):
print (index + 1, transition.title)
choice = int(raw_input('Choose one '))
if choice == 0:
exit(0)
else:
self.location = transitions[choice - 1]
def pirateShip(Object):
if pirateHp == 0:
print "You have defeated the pirates! Congradualations!"
pause
exit(1)
game = game()
game.play(game)
'game' object has no attribute 'introduction'
You should call the init of your super class when initializing game. In your current code, textadventure.init is never called which is why introduction is never added to textadventure.
Game should also not inherit from object (it does that through textadventure).
class game(textadventure):
def __init__(self):
super(game, self).__init__()
self.health = 100
def play(self):
print self.introduction
Should do the trick.
TypeError: play() takes exactly 2 arguments (1 given)
You never use your textadventure argument in play. Removing this should get things working.

Django Custom Model Field with Validation...how to hook it back to ModelForm

A common occurrence I have with one particular project is that it requires the user to enter dimensions (for width/depth/height) in Feet and Inches. Calculations are needed to be performed on that dimension, so I've been working on a custom field type that takes in a dimension in Feet/Inches (eg. 1'-10") and saves it to the database as a decimal number using a regex to parse the input. The field displays to the end-user as feet-inches at all times (with the eventual goal of writing a
method to be able to optionally display in metric, and interact with measure.py, and geodjango stuff). What I have so far is definitely not DRY, but aside from that, I'm having trouble with validation at the form level. The custom model field itself works properly (from what I've seen), and I've written a form field clean method which should work to validate the field. My question is how to hook that form field back into my model form to work for all the width/depth/height fields.
I'm thinking maybe an override of the init on the modelform (a la self.fields['depth']...) , but I'm not quite sure where to go from here...
DCML_PATTERN = re.compile(r'^(?P<feet>\d+)(?P<dec_inch>\.?\d*)\'?$')
FTIN_PATTERN = re.compile(r'^(?P<feet>\d+)\'?\s*-?\s*(?P<inch>[0-9]|10|11)?\"?$')
class FtInField(models.Field):
__metaclass__ = models.SubfieldBase
empty_strings_allowed = False
def db_type(self):
return 'double'
def get_internal_type(self):
return "FtInField"
def to_python(self,value):
if value is u'' or value is None:
return None
if isinstance(value, float):
m = FTDCML_PATTERN.match(str(value))
if m is None:
raise Exception('Must be an integer or decimal number')
feet = int(m.group('feet'))
dec_inch = float(m.group('dec_inch') or 0)
inch = dec_inch * 12
return "%d\'-%.0f\"" % (feet,inch)
return value
def get_db_prep_value(self,value):
if value is u'' or value is None:
return None
m = FTIN_PATTERN.match(value)
if m is None:
raise Exception('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
return (feet + (inch/float(12)))
class FtInField(forms.Field):
def clean(self,value):
super(FtInField, self).clean(value)
if value is u'' or value is None:
raise forms.ValidationError('Enter a dimension in X\'-Y" format')
m = FTIN_PATTERN.match(value)
if m is None:
raise forms.ValidationError('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
value = '%d\'-%.0f"' % (feet,inch)
return value
class ProductClass(models.Model):
productname = models.CharField('Product Name', max_length=60,blank=True)
depth = FtInField('Depth (Feet/Inches)')
width = FtInField('Width (Feet/Inches)')
height = FtInField('Height (Feet/Inches)')
class ProductClassForm(forms.ModelForm):
depth = FtInField()
width = FtInField()
height = FtInField()
class Meta:
model = ProductClass
class ProductClassAdmin(admin.ModelAdmin):
form = ProductClassForm
Thank you, thank you to both of you. This is what I came up with (based on both of your advise). I'll work on defining a data type to make it better in terms of repetition, but in the meantime, this works...(I was so close, yet so far away...) You guys are amazing. Thanks.
DCML_PATTERN = re.compile(r'^(?P<feet>\d+)(?P<dec_inch>\.?\d*)\'?$')
FTIN_PATTERN = re.compile(r'^(?P<feet>\d+)\'?\s*-?\s*(?P<inch>[0-9]|10|11)?\"?$')
class FtInFormField(forms.Field):
def clean(self,value):
super(FtInFormField, self).clean(value)
if value is u'' or value is None:
raise forms.ValidationError('Enter a dimension in X\'-Y" format')
m = FTIN_PATTERN.match(value)
if m is None:
raise forms.ValidationError('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
value = '%d\'-%.0f"' % (feet,inch)
return value
class FtInField(models.Field):
__metaclass__ = models.SubfieldBase
empty_strings_allowed = False
def db_type(self):
return 'double'
def get_internal_type(self):
return "FtInField"
def to_python(self,value):
if value is u'' or value is None:
return None
if isinstance(value, float):
m = FTDCML_PATTERN.match(str(value))
if m is None:
raise Exception('Must be an integer or decimal number')
feet = int(m.group('feet'))
dec_inch = float(m.group('dec_inch') or 0)
inch = dec_inch * 12
return "%d\'-%.0f\"" % (feet,inch)
return value
def get_db_prep_value(self,value):
if value is u'' or value is None:
return None
m = FTIN_PATTERN.match(value)
if m is None:
raise Exception('Must be in X\'-Y" Format')
feet = int(m.group('feet'))
inch = int(m.group('inch') or 0)
return (feet + (inch/float(12)))
def formfield(self, **kwargs):
defaults = {'form_class': FtInFormField}
defaults.update(kwargs)
return super(FtInField,self).formfield(**defaults)
class ProductClass(models.Model):
productname = models.CharField('Product Name', max_length=60,blank=True)
depth = FtInField('Depth (Feet/Inches)')
width = FtInField('Width (Feet/Inches)')
height = FtInField('Height (Feet/Inches)')
class ProductClassForm(forms.ModelForm):
class Meta:
model = ProductClass
class ProductClassAdmin(admin.ModelAdmin):
form = ProductClassForm
To avoid duplication you should probably implement a data type class that handles the parsing of feets and inches for you, it should greatly simplify the other code.
Then you should create a model field and form field, keeping in mind that these are two COMPLETELY SEPARATE components. (which you more or less have already done, but this is just for completeness)
Now, if I'm reading the question right, you want to set the default form field for your model field. To facilitate this you want to implement the formfield() function on your model field class. Ref: the django docs