Matching 3 out 5 fields - Django - python

I'm finding this a bit tricky! Maybe someone can help me on this one
I have the following model:
class Unicorn(models.Model):
horn_length = models.IntegerField()
skin_color = models.CharField()
average_speed = models.IntegerField()
magical = models.BooleanField()
affinity = models.CharField()
I would like to search for all similar unicorns having at least 3 fields in common.
Is it too tricky? Or is it doable?

You should use Q objects. The rough example is:
from django.db.models import Q
from itertools import combinations
# this -- the unicorn to be matched with
attr = ['horn_length', 'skin_color', 'average_speed', 'magical', 'affinity']
q = None
for c in combinations(attrs, 3):
q_ = Q(**{c[0]: getattr(this, c[0])}) & Q(**{c[1]: getattr(this, c[1])}) & Q(**{c[2]: getattr(this, c[2])})
if q is None:
q = q_
else:
q = q | q_
Unicorn.objects.get(q)
not tested, though

It has to be done in the HAVING clause:
SELECT ... HAVING (IF(a.horn_length=b.horn_length, 1, 0) + ...) >= 3
There's no way to express HAVING in the Django ORM so you'll need to drop to raw SQL in order to perform it.

This should cover your question, if I understood it right:
from django.db import models
Unicorn.objects.filter(models.Q(skin_color = 'white') | models.Q(magical = True))
This would filter all unicorns that have skin color white or have some magical stuff in common. More about the Q objects here http://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects

I have never used Django and i'm rather novice in Python but perhaps you can do something like this:
make a method that compares two instances of the class Unicorn.
def similarity(self, another)
sim = 0
if (self.horn_length==another.horn_length):
sim+=1
if (self.skin_color==another.skin_color):
sim+=1
if (self.average_speed==another.average_speed):
sim+=1
if (self.magical==another.magical):
sim+=1
if (self.affinity==another.affinity):
sim+=1
return sim
Then you can test with something like:
myUnicorn
for x in unicornsList:
if myUnicorn.similarity(x) >=3:
...

Related

Flask admin - complex sort on hybrid_property

I have a fairly complex hybrid_property. This is a vendor model, which has multiple skuchannels (products). What it does is: Based on the target_stock_duration (e.g. we want to keep items in stock for 4 months) calculate how many units have to be ordered and how much this would cost. This gives us the potential.
class Vendor(db.Model):
__tablename__ = "vendor"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(150))
b2c_price_factor = db.Column(db.Float, nullable=False)
skuchannels = db.relationship("SKUChannel", back_populates="vendor")
#hybrid_property
def po_potential(self):
"""This is a "virtual" property that will that can be used in the admin view. It calculates
the potential value for a comming PO.
Returns:
_type_: _description_
"""
potential = 0
for item in self.skuchannels:
purchasing_price = item.purchasing_price if item.purchasing_price != None else 0
target_stock_duration = 4
try:
to_order = item.average_monthly_sales * target_stock_duration - item.stock_level #calculate how many units we have to order
if to_order < 0:
to_order = 0
except TypeError:
to_order = 0
potential = potential + purchasing_price * to_order #calculate how much everything costs
return potential
well this hybrid_property works just fine, but I would very much like to sort this property. with #po_potential.expression -> well I have no clue how to do this, because in my understanding it should return a select object. Is there any other way?
This should get you started:
class Vendor(Base):
...
...
#po_potential.expression
def po_potential(cls):
target_stock_duration = 4
return (
select(func.sum(
func.ISNULL(SKUChannel.purchasing_price, 0) *
func.GREATEST(0, SKUChannel.average_monthly_sales * target_stock_duration - SKUChannel.stock_level, 0)
))
.where(SKUChannel.vendor_id == cls.id)
.label('po_potential')
)

How to instantiate a new property object for each new parent object?

I'm working a special situation where I'm trying to emulate the django feel of model classes like so:
class packet(models.packet):
field1 = models.IntField()
field2 = models.IntField()
There's a lot of background interfacing using metaclassing but the over idea is to allow the user to interact with the fields like so:
p = packet()
p.field1 = 12
p.field1 == 12 # true
while still not compromising the field type:
isinstance(p.field1, models.IntField) # true
The problem that I'm facing is that two packet objects share the same Fields since they're class properties:
p1 = packet()
p2 = packet()
p1.field1 = 12
p2.field2 = 14
p1 is p2 # false
p1.field1 is p2.field1 # true
How do I instantiate a new property object for each new parent object?
To give better context feel free to browse the source here

How can I combine/nest EnumFields in Django?

Django-EnumFields lets you combine Enum fields in Django (the clue was in the title).
Can you combine nest these?
Here's an example that plays off the docs:
from django.db import models
from django_enumfield import enum
class BeerStyle(enum.Enum):
LAGER = 0
STOUT = 1
WEISSBIER = 2
class SoftDrinkStyle(enum.Enum):
COKE = 3
LEMONADE = 4
class Drink(models.Model):
style = enum.EnumField(????, default=BeerStyle.LAGER)
I don't know what would go in place of ????, or if there is a better way to get this nested/combination to play out with Django. I'm mainly asking as I want Enum behaviour, with the ability to probe different types, e.g. in a save method, check for User age if the Drink is or type Beer.
Is this possible? Having played with this for a bit I don't see how.
Having looked over how Python Enums work, this looks like the best behaviour to mock up 'subclasses'
from django.db import models
from django_enumfield import enum
class DrinkStyle(enum.Enum):
LAGER = (0, 'Beer')
STOUT = (1, 'Beer')
WEISSBIER = (2, 'Beer')
COKE = (3, 'SoftDrink')
LEMONADE = (4, 'SoftDrink')
def __init__(self, id, drink_type):
self.id = id
self.type = drink_type
#property
def type(self):
return self.drink_type
class Drink(models.Model):
style = enum.EnumField(DrinkStyle, default=DrinkStyle.LAGER)
Then use DrinkStyle.COKE.type to return the type.

merge querysets in django

I have in models.py:
class Game(models.Model):
players1 = models.ManyToManyField(Player, related_name='games1')
players2 = models.ManyToManyField(Player, related_name='games2')
def get_all_players(self):
return list(itertools.chain(self.players1.all(), self.players2.all()))
How can I write same get_all_players method, but return QuerySet, not list?
P.S. I know that there is | operator:
def get_all_players(self):
return self.players1.all() | self.players2.all()
But it works in a very strange way. Result of this function contains more players than there are in players1 + players2 (result contains repeats of some players)
For a perhaps more semantically clear solution:
def get_all_players(self):
return (self.players1.all() | self.players2.all()).distinct()
This should do the trick:
# On the top of the file:
from django.db.models import Q
# Game instance method:
def get_all_players(self):
return Player.objects.filter(Q(games1__pk=self.pk) | Q(games2__pk=self.pk))
Q is described in details here: Complex lookups with Q objects.

How to search a Python List via Object Attribute?

Consider the following python class:
class Event(Document):
name = StringField()
time = DateField()
location = GeoPointField()
def __unicode__(self):
return self.name
Now I create a list of Events:
x = [Event(name='California Wine Mixer'),
Event(name='American Civil War'),
Event(name='Immaculate Conception')]
Now I want to add only unique events by searching via the event name.
How is this done with the boolean in syntax?
The following is incorrect:
a = Event(name='California Wine Mixer')
if a.name in x(Event.name):
x.append(a)
By unique I think you want something like "if a.name not in x(Event.name):", which can be written as
if not any(y.name == a.name for y in x):
...
But if the name acts as an index, it is better to use a dictionary to avoid the O(N) searching time and the more complex interface.
event_list = [Event(name='California Wine Mixer'), ...]
event_dict = dict((b.name, b) for b in event_list)
# ignore event_list from now on.
....
a = Event(name='California Wine Mixer')
event_dict.setdefault(a.name, a)

Categories

Resources