Django custom manager dont work - python

Based on this question i wrote this code:
class CourseManager(models.Manager):
def current(self):
return self.filter(date_start < datetime.datetime.now()).filter(date_end > datetime.datetime.now())
class PlannedCourse(models.Model):
course = models.ForeignKey(ECourse)
date_start = models.DateTimeField()
date_end = models.DateTimeField(blank=True)
...
objects = CourseManager()
def __str__(self):
return self.course.name + ' ' + self.date_start.strftime("%d-%m-%Y %H:%M") + ' - ' + self.date_end.strftime("%d-%m-%Y %H:%M")
but when I try to run PlannedCourse.objects.current() I get the error:
NameError: name 'date_start' is not defined.
I'm a newbie and don't understand why this happens :(

You should use fields lookups: __gt instead of > and __lt instead of <:
class CourseManager(models.Manager):
def current(self):
return self.filter(date_start__lt=datetime.datetime.now(),
date_end__gt=datetime.datetime.now())

You're comparing an undefined python object (date_start) with a date in python. However, the goal of a queryset is to compare objects in the database. Even if date_start was defined, this would be the equivalent of calling .filter(True) or .filter(False).
To tell Django to check in the database, you have to pass keyword arguments to the filter that are converted to a query by the ORM:
return self.filter(date_start__lt=datetime.datetime.now(),
date_end__gt=datetime.datetime.now())
Note that you're not actually comparing date_start and datetime.datetime.now(), the .filter function uses **kwargs, and gets the following arguments as a dictionary:
{
'date_start__lt': datetime.datetime.now(),
'date_end__gt': datetime.datetime.now(),
}
The __lt and __gt are field lookups that tell the queryset how to perform the comparison in the database.

Related

Django Max DateTime Aggregation

What is the best way of getting the max datetime of related objects of related objects of a Django instance? I need this value as an instance property.
For example, with these models,
class Show(Model):
is_live = BooleanField()
...
class Season(Model):
show = ForeignKeyField(Show, on_delete=CASCADE, related_name='seasons')
is_live = BooleanField()
...
class Episode(Model):
season = ForeignKeyField(Season, on_delete=CASCADE, related_name='episodes')
is_live = BooleanField()
live_date = DateTimeField(blank=True, null=True)
...
How could I get the most recent episode live_date of a show instance, including only episodes where the season is_live == True and the episode is_live == True?
I have tried this in the Show model:
#property
def max_episode_live_date(self)
return self.seasons.filter(
is_live=True
).aggregate(
max_=Max(
'episodes__live_date',
filter=Q(episodes__is_live=True)
)
).get('max_')
but I get this error
AttributeError: 'str' object has no attribute 'tzinfo'
I've tried using Django SubQuery expressions,
#property
def max_episode_live_date(self)
episodes = Episode.objects.filter(
is_live=True,
season=OuterRef('pk')
).values('live_date')
return self.seasons.filter(
is_live=True
).aggregate(
max_=Max(Subquery(episodes))
).get('max_')
but then I get django.db.utils.OperationalError: (1242, 'Subquery returns more than 1 row')
I believe this is due to some episodes containing null live_date, but I'm not sure how to work around. Any insight would be appreciated.
#property
def max_episode_live_date(self)
return self.seasons.filter(
is_live=True
).aggregate(
max_=Max(
'episodes__live_date',
filter=Q(episodes__is_live=True)
)
).get('max_')
The SQL
"SELECT MAX(CASE WHEN `episode`.`is_live` is true THEN `episode`.`live_date` ELSE NULL END) AS `max_`"
will be executed. The result will be a string or None. When a string is converted to DateTime with timezone, the error happens.
I think it may be a bug of Django.

get all from table django not only __str__(self)

models.py
class club(models.Model):
name = models.CharField(max_length=30)
city = models.CharField(max_length=30)
premiere_leauge = models.BooleanField(default=False)
def __str__(self):
return self.name
Views.py
...
a = request.POST['club']
b = request.POST['city']
result = club.objects.all.get(name__exact=a, city__exact=b)
....
All is fine, however I believe result returns me:
def __str__(self):
return self.name
Whatsover, I would like an equivalent of 'SELECT * FROM APP_CLUB where name='something and city='something'; so i would be able to do further manipulation in django like:
if result[3] is True:
do_something()
As suggested in the offical documentation:
club.objects.filter(name='something')
It will return exactly the same as:
'SELECT * FROM APP_CLUB where name='something';
Example:
clubs = club.objects.filter(name__exact='something')
for club in clubs:
if club.premier_league:
do_something()
If you want to get only one club, then do it like this:
club = club.objects.get(name='something')
premier_league_value_of_club = club.premier_league
Use filter instead of get.
results = club.objects.filter(name__exact=a, city__exact=b)
You can then iterate over it to access all the model attributes like below
for result in results:
print result.name, result.city, result.premier_league
Also, according to PEP-8, you should name your class name should ideally be titlecase Club instead of lowercase club.
You're nearly there, I think you're missing the filter function here. You can use it like this :
a = request.POST['club']
b = request.POST['city']
result = club.objects.filter(name__exact=a, city__exact=b)
It will return you a query set with the actual database entries.
The __str__(self) function is used in transforming your query set entry into a string, whether you string-cast it or print it.
Then about this :
if result[3] is True:
do_something()
I don't get well what you mean about this, but if 3 is the id of the entry in the database, you then can do this :
if result.get(id=3).premiere_leauge:
do_something()
But you might want to check if the entry with the id exists first to avoid errors :
if result.filter(id=3).exists() and result.get(id=3).premiere_leauge:
do_something()
You should modify you query as shown below
result = club.objects.filter(name__exact=a, city__exact=b)
Good Luck !!

Looking for ForeignKey active and add to QuerySet

class Control(models.Model):
period = models.DurationField()
active = models.BooleanField()
device_collection = models.ForeignKey(DeviceSet)
class DeviceSet(models.Model):
name = models.CharField()
date_last_control = models.DateField()
def get_next_control(self):
return self.date_last_control + self.control_actif.period
#property
def control_actif(self):
if not hasattr(self, "_control"):
setattr(self, "_control", self.control_set.get(active=True))
return self._control
There are several Control associated with DeviceSet but only one Control which is active by DeviceSet.
I'd like to get the active Control of the DeviceSet when I get the queryset in a column _control.
I already try :
DeviceSet.objects.annotate(_control = Q(control__active=True))
That don't work
'WhereNode' object has no attribute 'output_field'
And after set output_field=Control I have the following exception:
type object 'Control' has no attribute 'resolve_expression'
I just want to have like a prefetch_related with filter but in a new column to use the _control attribute in model's method.
You are getting errors from what you've attempted because annotate method needs an aggregate function (eg Sum, Count etc) rather than a Q object.
Since Django 1.7 it's possible to do what you want using prefetch_related, see docs here:
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.Prefetch
DeviceSet.objects.prefetch_related(
Prefetch('control_set',
queryset=Control.objects.filter(active=True),
to_attr='_control')
)

Django - Use getattr() to retrieve Model Method (#property)

I would like to do this:
def retrieve_data(self, variable):
start_date = datetime.date.today() - datetime.timedelta(int(self.kwargs['days']))
invoice_set = InvoiceRecord.objects.filter(sale_date__gte=start_date)
total = 0
for invoice in invoice_set:
for sale in invoice.salesrecord_set.all():
total += getattr(self, variable)
return round(total)
Where variable is submitted as a string that represents one of my model methods:
#property
def total_sale(self):
return self.sales_price * self.sales_qty
But my effort doesn't work:
def total_sales(self):
return self.retrieve_data(variable="total_sale")
It simply says:
'SalesSummaryView' object has no attribute 'total_sale'
Evidently, I am misunderstanding the usage. Can someone help me figure out a way to accomplish this goal?
Got it! I was calling getattr() on the view, rather than the model. Instead of using self, I needed to submit the sale object.
for invoice in invoice_set:
for sale in invoice.salesrecord_set.all():
total += getattr(sale, variable)

Convert mongodb return object to dictionary

I'm using the bottle framework together with mongoengine.
I have an orders model :
class OrderDetail(Option):
orderDetailsQty = FloatField()
def to_dict(self):
return mongo_to_dict_helper(self)
class Order(Document):
userName = StringField(required=True)
orderDate = DateTimeField()
orderStatus = ListField(EmbeddedDocumentField(Status))
orderDetails = ListField(EmbeddedDocumentField(OrderDetail))
orderComments = ListField(EmbeddedDocumentField(Comment))
isActive = BooleanField()
def to_dict(self):
orderObj = mongo_to_dict_helper(self)
orderDetailList = []
for orderDetail in orderObj["orderDetails"]:
orderDetailList.append(orderDetail.__dict__)
orderObj["OrderDetails"] = orderDetailList
return (self)
When mongodb is queried I get an object which is then converted in to a dict by using the following function :
def mongo_to_dict_helper(obj):
return_data = []
for field_name in obj._fields:
if field_name in ("id",):
continue
data = obj._data[field_name]
if isinstance(obj._fields[field_name], StringField):
return_data.append((field_name, str(data)))
elif isinstance(obj._fields[field_name], FloatField):
return_data.append((field_name, float(data)))
elif isinstance(obj._fields[field_name], IntField):
return_data.append((field_name, int(data)))
elif isinstance(obj._fields[field_name], ListField):
return_data.append((field_name, int(data)))
else:
# You can define your logic for returning elements
pass
return dict(return_data)
I found this function after a long search in the internet. Later found out that this function also fails while defining a member as the ListField(EmbeddedDocumentField(obj)).
I also tried writing a condition for catching the specific case of EmbeddedDocumentField :
elif isinstance(obj._fields[field_name], EmbeddedDocumentField):
return_data.append(mongo_to_dict_helper(data))
but that didn't do any good either.
Anyone have a workaround for this issue ?
What about just using to_mongo method of an object to convert it to a dict?
object.to_mongo()
Expanding on #alexvassel's and #z0r's answers, calling .to_mongo() converts the object to a SON instance. Once you have it, you can call its .to_dict() method to convert it to a dictionary.
For example... (qset is a queryset that's returned from mongoengine, after e.g. Posts.objects.all()).
sons = [ob.to_mongo() for ob in qset]
for son in sons:
print str(son.to_dict())
import json
json.loads(yourobj.to_json())
Extending on #alexvassel's answer, to_mongo() method returns SON object, which you can convert to dict by calling its to_dict() method
object.to_mongo().to_dict()
you can custom method to convert object to dict
class Order(Document):
userName = StringField(required=True)
orderDate = DateTimeField()
orderStatus = ListField(EmbeddedDocumentField(Status))
orderDetails = ListField(EmbeddedDocumentField(OrderDetail))
orderComments = ListField(EmbeddedDocumentField(Comment))
isActive = BooleanField()
def as_dict(self):
return {
"user_name": self.userName,
"order_date": self.orderDate.strftime("%Y-%m-%d %H:%M:%S"),
}
now you can use obj.as_dict() to dict
orders = Order.objects.all()
datas = [each.as_dict() for each in orders]
combining all other answers,
import json
dict = {'data':[json.loads(ob.to_json()) for ob in qset]}
There can be two scenario.
when query returns CommandCursor object
**records = list(CursorObject)**
ex - Class.objects().aggregate({...})
when query returns BaseQuerySet object
**import json**
**records = json.loads(BaseQuerySetObject.to_json())**
ex - Class.objects().filter(..)

Categories