How to avoid date overlaps in Django models? - python

I have a model:
class Dimension_Item(models.Model):
uq_dimension_item_id = MyCharField(max_length=1024, primary_key=True)
dimension_id = MyCharField(max_length=1024)
dimension_item_id = MyCharField(max_length=100)
name = MyCharField(max_length=255)
description = MyCharField(max_length=512, null = True)
start_date = models.DateField(default=date(1,1,1))
end_date = models.DateField(default=date(9999,12,31))
function for adding information to the model:
def addRows(in_args):
rq = in_args[0]
pk_l=[]
rows = rq['rows']
if type(rows).__name__ == 'dict':
dim = Dimension_Item(
name=rows['name'],
start_date=rows['start_date'],
end_date=rows['end_date']
)
dim.save()
pk_l.append(dim.dimension_id)
elif type(rows).__name__ == 'list':
for i in rows:
dim = Dimension_Item(
name=rows['name'],
start_date=rows['start_date'],
end_date=rows['end_date']
)
dim.save()
pk_l.append(dim.dimension_id)
else:
pass
return getRows(in_args, pk_l)
# return "success add row"
clauses.addMethod(addRows)
and function for modifying the model items:
def modifyRows(in_args):
pk_l=[]
rq = in_args[0]
rows = rq['rows']
if type(rows).__name__ == 'dict':
dim = Dimension_Item.objects.get(pk=rows['pk'])
for attr in rows:
if attr!='pk':
try:
setattr(dim, attr, rows[attr])
except KeyError:
pass
dim.save()
pk_l.append(dim.dimension_id)
elif type(rows).__name__ == 'list':
for i in rows:
dim = Dimension_Item.objects.get(pk=i['pk'])
for attr in i:
if i!='pk':
try:
setattr(dim, attr, i[attr])
except KeyError:
pass
dim.save()
pk_l.append(dim.dimension_id)
else:
pass
return getRows(in_args, pk_l)
# return "success modify"
clauses.addMethod(modifyRows)
I should check if start_date and end_date fields don't overlap other records in the database.
For example, I enter: 2.02.1988-3.07.1989. And if I already have the record with 2.07.1989 - 3.08.1990, I have to throw an exception about date overlapping.
How better can I do it?

I would override the save() method of your Dimension_Item model.
In your custom save() method you can implement your check for overlapping dates. If everything is OK, create the object. If not, just return nothing (or raise and error.)
The Django documentation explains it really good: https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-model-methods
Here is some (untested) code to get you started:
class Dimension_Item(models.Model):
start_date = models.DateField(default=date(1,1,1))
end_date = models.DateField(default=date(9999,12,31))
def save(self, *args, **kwargs):
# get number of items that have an overlapping start date
dimension_items_overlapping_start = Dimension_Item.objects.filter(start_date__gte=self.start_date, start_date__lte=self.end_date).count()
# get number of items that have an overlapping end date
dimension_items_overlapping_end = Dimension_Item.objects.filter(end_date__gte=self.start_date, end_date__lte=self.end_date).count()
overlapping_dimension_items_present = dimension_items_overlapping_start > 0 or dimension_items_overlapping_end > 0
if overlapping_dimension_items_present:
return
else:
super(Dimension_Item, self).save(*args, **kwargs) # Call the "real" save() method.

class Dimension_Item(models.Model):
start_date = models.DateField(default=date(1,1,1))
end_date = models.DateField(default=date(9999,12,31))
def save(self, *args, **kwargs):
# check for items that have an overlapping start date
dimension_items_overlapping_start = Dimension_Item.objects.filter(start_date__gte=self.start_date, start_date__lte=self.end_date).exists()
# check for items that have an overlapping end date
dimension_items_overlapping_end = Dimension_Item.objects.filter(end_date__gte=self.start_date, end_date__lte=self.end_date).exists()
# check for items that envelope this item
dimension_items_enveloping = Dimension_Item.objects.filter(start_date__lte=self.start_date, end_date__gte=self.end_date).exists()
overlapping_dimension_items_present = dimension_items_overlapping_start or dimension_items_overlapping_end or dimenstion_items_enveloping
if overlapping_dimension_items_present:
return
else:
super(Dimension_Item, self).save(*args, **kwargs) # Call the "real" save() method.

Related

Django jsonfield persisting old object values in a loop

Here is the model:
class ModelA(models.Model):
field1 = models.CharField(max_length=100)
field2 = models.CharField(max_length=100)
field3 = JSONField(default=[])
def save(self, *args, **kwargs):
# Below print should be None
# But it shows the value of the previously created object, why ?
print "------------------------------------------------"
print self.id
print "self.field3"
print self.field3
self.field2 = self.field1 + " world"
self.field3.append(self.field2)
super(ModelA, self).save(*args, **kwargs)
Here is the view:
def view1(request):
for x in range(1, 3):
a = ModelA.objects.create(field1="hello%s" % x)
Expected output:
# None
# self.field3
# []
# None
# self.field3
# []
Achieved output:
# None
# self.field3
# []
# None
# self.field3
# [u'Hello1 world']
# None
# self.field3
# [u'Hello1 world', u'Hello2 world']
So, as per the given output, can you tell me why its using previous objects values while creating a new object ?
This is from Django documentation:
If you give the field a default, ensure it’s a callable such as dict
(for an empty default) or a callable that returns a dict (such as a
function). Incorrectly using default={} creates a mutable default that
is shared between all instances of JSONField.
Therefore use this instead:
field3 = JSONField(default=list)

Read MongoEngine DynamicDocuments

my issue is that I am saving dict objects with MongoEngine:
class MongoRecord(DynamicDocument):
record_id = SequenceField(primary_key = True)
class SimpleMongo(object):
def __init__(self, *args, **kwargs):
"""
Very simple dict-like Mongo interface
"""
if PY_VERSION == 2:
self.iterattr = 'iteritems'
else:
self.iterattr = 'items'
self.debug = DEBUG
self.dict_type = type(dict())
self.dbname = kwargs.get('dbname', 'untitled')
self.collection_name = kwargs.get('collection', 'default')
self.ip = kwargs.get('ip', '127.0.0.1')
self.port = kwargs.get('port', 27017)
self.dbconn = connect(self.dbname, host=self.ip, port=self.port)
drop = kwargs.get('drop', False)
if drop:
self.dbconn.drop_database(self.dbname)
def put(self, data):
"""
Put dict
"""
assert type(data) == self.dict_type
record = MongoRecord()
record.switch_collection(self.collection_name)
generator = getattr(data, self.iterattr)
__res__ = [setattr(record, k, v) for k,v in generator()] # iteritems() for Python 2.x
record.save()
but when trying to access them:
def get(self):
record = MongoRecord()
record.switch_collection(self.collection_name)
return record.objects
getting
mongoengine.queryset.manager.QuerySetManager object, not an iterator.
So, what is the proper way to get my data back from Mongo being saved as DynamicDocument?
The problem isn't that MongoRecordis a DynamicDocument or that it contains a dict. You would get the same result with a regular Document. Your problem is with querying, you should change record.objects to MongoRecord.objects to get a cursor.
Regarding your usage of switch_collection()...
If MongoRecord documents will be saved to a collection with the same name, at most times, you can define this like below, and you don't have to use switch_collection() when a collection with that name is being queried.
class MongoRecord(DynamicDocument):
record_id = SequenceField(primary_key = True)
meta = {'collection': 'records'}
In case you do want to retrieve MongoRecord documents from a collection which isn't called 'records', and you want to define a function for this (which can give an UnboundLocalError), you can do it like this (source):
from mongoengine.queryset import QuerySet
def get(self):
new_group = MongoRecord.switch_collection(MongoRecord(), self.collection_name)
new_objects = QuerySet(MongoRecord, new_group._get_collection())
all = new_objects.all()
# If you would like to filter on an MongoRecord attribute:
filtered = new_objects.filter(record_id=1)
return all

Django Model field calculated from other fields

There are quite a few other questions here around this but none of them have helped.
I would like a yield field to be calculated by yield = starts / finishes fields. I'd like to graph it using Highcharts.
However when I add an object via Admin portal - it's setting yield = 0 and won't calculate.
models.py:
class wYield(models.Model):
starts = models.PositiveIntegerField(default=0)
finishes = models.PositiveIntegerField(default=0)
## ONE: i've tried the below:
yield_num = model.FloatField()
def save(self, *args, **kwargs):
if self.starts > 0:
self.yield = self.finishes / self.starts
else:
self.yield = 0
super(wYield, self).save(*args, **kwargs)
## TWO: i've tried this but then I don't know how to see it in the Admin view:
def _set_yield(self):
if self.starts > 0:
x = self.finishes / self.starts
else:
x = 0
return x
yield_num = property(_set_yield)
## THREE: I think this works the same as TWO
#property
def yield_num(self):
if self.starts > 0:
return self.finishes / self.starts
else:
return 0
admin.py:
from .models import wYield
class wYieldAdmin(admin.ModelAdmin):
list_display = ('starts', 'finishes', 'yield_num')
admin.site.register(wYield, wYieldAdmin)
Figured it out (w the help of AbhiP!) Python int typecasting was the main culprit. Stupid problem to have!
Below is what worked, and allowed me to not save the calculated field (but display it and make it act like a field in the model):
#property
def yield_num(self):
if self.starts > 0:
#needed the float() to cast it out of an int
return self.finishes / float(self.starts)
else:
return 0
Maybe you don't have to save it on the db every time as it can be calculated on the fly; otherwise you'd be wasting space. Not to mention duplicity in the db values.
Also, if tomorrow your logic changes for yield you will have full freedom to do it on to your lambda expressions. Here's one example:
Class wYieldAdmin(admin.ModelAdmin):
yield = lambda self: (self.finishes / self.starts) if self.starts > 0 else 0
yield.short_description = 'Yield'
list_display = ('starts', 'finishes', 'yield')
Class wYieldAdmin(admin.ModelAdmin):
yield = lambda self: (self.finishes / self.starts) if self.starts > 0 else 0
yield.short_description = 'You have a big head'
list_display = ('starts', 'finishes', 'yield')

Django models with a many to many relationship causing conflicting ordering requirements in models.py

I have issues with the two following models. The first model (QueryJob) is referenced by the second model(PropertyQuery) by a many to many field( queryID = models.ForeignKey(QueryJob,blank=True,null=True)) which necessitates that QueryJob precedes PropertyQuery.
How PropertyQuery objects are created by calling QueryJob.createqueries() which requires that PropertyQuery preceeds QueryJob.
Code is below. Is there a better way to approach this problem?
class QueryJob(models.Model):
queryUID = models.AutoField(primary_key=True)
client = models.ManyToManyField(Client,blank=True)
Json = JSONField()
street_column =models.TextField()
state_column =models.TextField(blank=True)
suburb_column =models.TextField(blank=True)
postcode_column =models.TextField(blank=True)
def createqueries(self):
#json_data = json.loads(self.Json)
print self.Json
for each in self.Json:
try:
Street = each[self.street_column]
State = each[self.state_column]
Suburb = each[self.suburb_column]
Postcode = each[self.postcode_column]
q = PropertyQuery(street_address = Street, state=State ,suburb = Suburb,postcode=Postcode,queryID=self.queryUID )
q.save()
except:
pass
def save(self, *args, **kwargs):
self.createqueries()
super(QueryJob, self).save(*args, **kwargs)
Second model
class PropertyQuery(models.Model):
queryID = models.ForeignKey(QueryJob,blank=True,null=True)
linkedproperty = models.ForeignKey(Property,blank=True,null=True)
street_address = models.CharField(max_length=255, db_index=True,null=True)
suburb = models.CharField(max_length=120, db_index=True,blank=True,null=True)
state = models.CharField(max_length=3, db_index=True,blank=True,null=True)
postcode = models.IntegerField(max_length=4, db_index=True, blank=True,null=True)
matcheduncertainty = models.PositiveSmallIntegerField(blank=True,null=True)
def search_for_a_match(self):
if self.postcode:
print self.postcode
print Property.objects.filter(postcode=self.postcode)
try:
property_list = Property.objects.filter(postcode=self.postcode)
print property_list
except:
print "no properties in that postcode"
return
elif self.suburb:
try:
property_list = Property.objects.filter(suburb=self.suburb)
print property_list
except:
print "no properties in that suburb"
elif self.state:
try:
property_list = Property.objects.filter(state=self.state)
print property_list
except:
print "no properties in that state"
return
else:
print "no properties found"
return
for possible in property_list:
if possible.street_address == self.street_address:
self.linkedproperty = possible
self.matcheduncertainty = 100
return
else:
print "we will need to try something else"
Switch the order of these statements, so that your QueryJob is created first, then your PropertyQuery models are created:
def save(self, *args, **kwargs):
super(QueryJob, self).save(*args, **kwargs)
self.createqueries()
In your createqueries() method, you can refer to self when you need to create a link. Do not create a link to the primary key directly as this won't work - you don't realize its not working because you have a blank except clause that is catching the exceptions raised:
def createqueries(self):
#json_data = json.loads(self.Json)
print self.Json
for each in self.Json:
Street = each.get(self.street_column)
State = each.get(self.state_column)
Suburb = each.get(self.suburb_column)
Postcode = each.get(self.postcode_column)
q = PropertyQuery(street_address = Street,
state=State,
suburb = Suburb,
postcode=Postcode,
queryID=self)
q.save()

Learning how to use the deferred library

I'm trying code nearly identical to the example from the manual to enable a download counter but I get an exception:
File "/media/Lexar/montao/wwwblob/handler.py", line 117, in FileInfo
download_count = db.IntegerProperty(required=True, count=0) TypeError: init() got an unexpected keyword argument 'count'
Here's the code I try to run:
from google.appengine.ext import deferred
from google.appengine.runtime import DeadlineExceededError
class Mapper(object):
# Subclasses should replace this with a model class (eg, model.Person).
KIND = None
# Subclasses can replace this with a list of (property, value) tuples to filter by.
FILTERS = []
def __init__(self):
self.to_put = []
self.to_delete = []
def map(self, entity):
"""Updates a single entity.
Implementers should return a tuple containing two iterables (to_update, to_delete).
"""
return ([], [])
def finish(self):
"""Called when the mapper has finished, to allow for any final work to be done."""
pass
def get_query(self):
"""Returns a query over the specified kind, with any appropriate filters applied."""
q = self.KIND.all()
for (prop, value) in self.FILTERS:
q.filter('%s =' % prop, value)
q.order('__key__')
return q
def run(self, batch_size=100):
"""Starts the mapper running."""
self._continue(None, batch_size)
def _batch_write(self):
"""Writes updates and deletes entities in a batch."""
if self.to_put:
db.put(self.to_put)
self.to_put = []
if self.to_delete:
db.delete(self.to_delete)
self.to_delete = []
def _continue(self, start_key, batch_size):
q = self.get_query()
# If we're resuming, pick up where we left off last time.
if start_key:
q.filter('__key__ >', start_key)
# Keep updating records until we run out of time.
try:
# Steps over the results, returning each entity and its index.
for (i, entity) in enumerate(q):
(map_updates, map_deletes) = self.map(entity)
self.to_put.extend(map_updates)
self.to_delete.extend(map_deletes)
# Do updates and deletes in batches.
if (i + 1) % batch_size == 0:
self._batch_write()
# Record the last entity we processed.
start_key = entity.key()
self._batch_write()
except DeadlineExceededError:
# Write any unfinished updates to the datastore.
self._batch_write()
# Queue a new task to pick up where we left off.
deferred.defer(self._continue, start_key, batch_size)
return
self.finish()
class FileInfo(db.Model):
blob = blobstore.BlobReferenceProperty(required=True)
download_count = db.IntegerProperty(required=True, count=0)
uploaded_by = db.UserProperty(required=True)
uploaded_at = db.DateTimeProperty(required=True, auto_now_add=True)
class DailyTotal(db.Model):
date = db.DateProperty(required=True, auto_now_add=True)
file_count = db.IntegerProperty(required=True)
download_count = db.IntegerProperty(required=True)
class DownloadCountMapper(Mapper):
KIND = FileInfo
def __init__(self):
self.file_count = 0
self.download_count = 0
def map(self, file):
self.file_count += 1
self.download_count += file.download_count
def finish(self):
total = DailyTotal(file_count=self.file_count,
download_count=self.download_count)
total.put()
Can you tell me what I should do?
Thank you
This line is the culprit:
download_count = db.IntegerProperty(required=True, count=0)
The IntegerProperty constructor doesn't know what to do with count. Maybe you meant this:
download_count = db.IntegerProperty(required=True, default=0)

Categories