Django smart/chained select manytomanyfield - python

here is the deal, this is my model:
class Health_plan(models.Model):
a = models.IntegerField ()
b = models.IntegerField ()
c = models.IntegerField ()
class Doctors_list(models.Model):
specialty = models.CharField(max_length=15)
name = models.CharField(max_length=30)
hp_id = models.ManyToManyField(Health_plan)
location = models.CharField(max_length=15)
def __unicode__(self):
return self.name
So, i have a table named Doctors_list, a doctor has one specialty, one location, one name, and a list of health plans covered.
To manage this, i have a table with the health plans on the columns (a,b,c). The doctors are identified in this list by their id, and the rows contain 1 or 0, 1 for health plan covered, 0 for not covered. like this:
ID A B C
1 0 0 1
2 1 0 0
3 1 0 1
Is there a better way to make this relation???
The user first chooses the specialty, my form:
class SpecForm(ModelForm):
a = Doctors_list.objects.values_list('specialty', flat=True)
unique = [('---------------','---------------')] + [(i,i) for i in set(a)]
specialty = forms.ChoiceField(choices=unique)
class Meta:
model = Doctors_list
The big thing is: a smart select for the relation specialty/health_plans.
The user chooses the specialty.
The specialty is covered by one or more doctors.
I use those doctor(s) id, and go check on health_plan table wich plans are available.
The user select's the plan.
I will keep researching but any tip is gratefully welcomed.
I hope i made myself clear.

What you describe is a misuse of the ManyToMany relation.
If you want to create a many-to-many relation between A and B, all you have to do is add a ManyToMany field in the definion of one of the models (A or B) and django will take care of the rest. Specifically, it will create a 3rd table where it will store the associations in the form of (A.id, B.id). This is standard rational database procedure for this type of relations, and Django is implementing it.
See http://en.wikipedia.org/wiki/Junction_table for the general definition of a many to many relation.
https://docs.djangoproject.com/en/1.5/ref/models/fields/#manytomanyfield - for the Django implementation.

Related

How to use django-filter package for foreign key fields in Django?

Hi all!
New in Django, and confused, help is appreciated! I've created a table, , thanks to a stackoverflow users, like:
Organization
Total amount of appeals
Amount of written form appeals
Amount of oral form appeals
Organization 1
3
1
2
Organization 2
2
1
1
Have three models:
class Organization(models.Model):
organization_name = models.CharField(max_length=50)
class AppealForm(models.Model):
form_name = models.CharField(max_length=50)
class Appeal(models.Model):
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
appeal_form = models.ForeignKey(AppealForm, on_delete=models.CASCADE)
applicant_name = models.CharField(max_length=150)
appeal_date = models.DateField()
Objects of Organization model:
organization_name
Organization 1
Organization 2
Objects of AppealForm model:
form_name
In written form
In oral form
Objects of Appeal model:
organization
appeal_form
applicant_name
Organization 1
In written form
First and Last name
Organization 1
In oral form
First and Last name
Organization 1
In oral form
First and Last name
Organization 2
In written form
First and Last name
Organization 2
In oral form
First and Last name
In the function of views.py file, I've created a query to render, like:
from django.db.models import Count, Q
organizations = Organization.objects.annotate(
).annotate(
total=Count('appeal'),
total_written=Count('appeal', filter=Q(appeal__appeal_form__form_name='in written form')),
total_oral=Count('appeal', filter=Q('appeal__appeal_form__form_name='in oral form'))
)
And now I want to filter table contents by AppealForm model and date of appeals (appeal_date field of Appeal model). Case: User opens a table and from search bar above the table chooses which appeal form and/or the range of dates to see.
Question: How to filter the query which is above in views.py using django-filter package?
The most general way to define a complicated filter is to use the method argument. I can't say I completely understand your problem, but you can apply any filter for which you can dream up a queryset in this way. In outline:
import django-filters as DF
class SomeFilters( DF.FilterSet):
name = DF.xxxFilter( method='my_method', field_name='object_field', label='whatever', ...)
...
def my_method( self, qs, name, value):
# in here you create a new more restrictive queryset based on qs
# to implement your filter, and return it.
# name is the field name. Note, you don't have to use or follow it
# value is the value that the user typed
qs = qs.filter( ...) # or .exclude, or complicated stuff
return qs
Here's a fairly simple method that I wrote to create an annotation with the value of a field stripped of spaces, and then to do a text-contains filter on that.
def filter_array_desc( self, qs, name, value):
value = value.replace(' ','')
qs = qs.annotate(
arr_d_nospaces = Replace( 'array_desc', Value(' '), Value('')) # delete all spaces
).filter(
arr_d_nospaces__icontains = value )
return qs
Here's a general one that can be applied to any field through a ChoiceFilter to filter whether the field is blank or not:
YESNO = (('Y','Yes'), ('N','No'))
marr_b = FD.ChoiceFilter( field_name='marr', label='M_array is blank', method='filter_blank_yesno',
choices=YESNO, empty_label="Don't Care" )
...
def filter_blank_yesno( self, qs, name, value):
if value=="Y":
return qs.filter(**{ name:'' })
elif value=="N":
return qs.exclude( **{ name:'' })
raise ValueError(f'filter_blank_yesno received value="{value}" which is neither "Y" nor "N"')
Hope this helps. You will basically be filtering by following relationships between your models, using double-underscores to move between models, and possibly annotating and filtering on the anotation, or doing things with Q objects and suchlike.

How to filter in Django DB based on calculations from different tables

I have 3 models from 3 different tables:
class Profile(models.Model):
player_name = models.CharField(max_length=150)
player_surname=models.CharField(max_length=200)
class Meta:
db_table='profiles'
class Results_2019(models.Model):
player_name = models.CharField(max_length=150)
first_score=models.DecimalField(decimal_places=2,max_digits=1000)
second_score=models.DecimalField(decimal_places=2,max_digits=1000)
class Meta:
db_table='results_2019'
class Results_2018(models.Model):
player_name = models.CharField(max_length=150)
first_score=models.DecimalField(decimal_places=2,max_digits=1000)
second_score=models.DecimalField(decimal_places=2,max_digits=1000)
class Meta:
db_table='results_2018'
The database is static. Player in all three models have the same names =['Alex','Chandler','Zach'] and they all have data belonging to them for all columns ( first_score,second_score)
Profile database look like :
id player_name player_surname
1 Alex Chapman
2 Chandler Wellington
3 Zach Berg
Results_2019 database look like :
id profile_id player_name first_score second_score
1 1 Alex 70 68
2 2 Chandler 60 62
3 3 Zach 90 85
Results_2018 database look like :
id profile_id player_name first_score second_score
1 1 Alex 78 81
2 2 Chandler 56 66
3 3 Zach 97 95
I want to make calculations based on values from Results_2019 and Results_2018 to get:
- players whos growth in first_score and second_score in 2019 and in 2018 were more than 20% and then get the name and surnames who met these criteria and print in template.
The below formula of criteria though does not work i show as guidance of what i want to achieve.
first_score_growth=((Results_2019.first_score/Results_2018.first_score)*100)
and
second_score_growth=((Results_2019.second_score/Results_2018.second_score)*100)
What i could do so far :
For this purpose i assigned Foreign key thorugn DB i have seperate column in mysql prodile_id. However it does not do even migrations.
What i could calculate and filter results from 1 DB table (Results_2019):
total=Results_2019.objects.annotate(
total_score=(F('first_score') + F('second_score'))/2).filter(total_score__gt=80,)
But this calculates total score for year 2019 but not the growth.
Also when i add foreign key to Results_2019 it gives me such OperationalError (1054, "Unknown column 'Results_2019.profile_id' in 'field list'")
I would appreciate your help to sort this out.
Firstly, I would suggest restructuring your models so that the results link to the profile and that you have a single results model with a field for the year. I see you have the player name in your results classes, but this can be accessed via the relationship to the Player model such as result.player.firstname. As a side note, models are usually referred to in the singular, i.e. Profile, not Profiles.
class Player(models.Model):
firstname = models.CharField(max_length=150)
surname = models.CharField(max_length=200)
class Result(models.Model):
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name='results')
year = models.IntegerField() # this is where you would store 2018 or 2019
score = models.DecimalField(decimal_places=2, max_digits=1000)
I have also removed the first_score and second_score so that each result just has a single score field. You would therefore have a Result object for each score and year which should make it easier to use annotate, aggregate, and filter.
You're certainly on the right lines of using annotate to create your desired growth value but this is hampered by your model layout. Once you've restructured your models, you can get the growth like this:
score1 = Avg('results__score', filter=Q(results__year=2018))
score2 = Avg('results__score', filter=Q(results__year=2019))
player_growth = Player.objects.values().annotate(growth=(score2/score1)*100)
Note that using 'results__score' uses the related name set in the player field and gets the score from each result linked to that player.
The above should return something like this (using dummy data):
{'id': 1, 'firstname': 'John', 'surname': 'Smith', 'growth': Decimal('110.0')}
Edit: If you aren't able to change the model structure then here's something else that will work, adding a method to the Player model. Note, you should change the Result relationship to a OneToOneField.
def get_first_score_growth(self):
return self.results_2019.first_score / self.results_2018.first_score * 100
You can call this in a template like so:
{% for player in players %}
{{ player.get_first_score_growth }}
{% endfor %}
Your model structure seems very complicated. Why are you creating different table for each year? Just add a year field in Result table and this should do your work.
Model
class Result(models.Model):
player=models.ForeignKeyField(Profiles)
first_score=models.DecimalField(decimal_places=2,max_digits=1000)
second_score=models.DecimalField(decimal_places=2,max_digits=1000)
year=models.CharField(max_length=4)
class Meta:
db_table='result'
Now you can write your query to get desired data.
Depending on whether or not you will need to do this calculation once or multiple times, I might create a new column. Creating a new column now will make this easier for future calculations. If you do not want to create a new column, you could find which people grew in each year and make a list.
If they appear in both lists, then you know they grew in both 2018 and 2019.
def myPageView(request):
GrowthNames = []
# loop through both tables
for MyResults in [Results_2018, Results_2019]:
# Get all objects and calculate growth
results_list = MyResults.objects.all()
for result in results_list:
score1 = result.first_score
score2 = result.second_score
growth = (score2 - score1) / score1
# if growth is > 20%, add it to our list
if growth > .02:
GrowthNames.append(results.player_name)
# Now, we must find which items appear in our list twice (once for 2018, once for 2019)
DoubleGrowthNames = list(set([x for x in GrowthNames if GrowthNames.count(x) == 2])
# With our list our names, we can now get their surnames via querying
FinalNames = []
for name in DoubleGrowthNames:
MyProfile = Profile.objects.filter(player_name=name)[0]
FinalNames.append(f'{MyProfile.player_name} {MyProfile.player_surname}')
# Finally, let's return this to the template
return render(request, 'page.html', {'FinalNames': FinalNames})
In page.html:
{% for name in FinalNames %}
<div>{{name}}</div>
{% endif %}

How to write select query in django in chain of one-to-many relationship?

How to write select query in django? I have 2 one-to-may relationship
At the beginning, i am not very good at english. i am so sorry :). I have 3 tables. Driver, Car and Ride. The relationship between Driver and Car is (one-to-many: a Driver can have multiple Car. The relationship between Car and Ride is (one-to-many: a Car can have multiple Ride. I want list of drivers who have x times of rides.
My Models:
class Driver(models.Model):
account = GenericRelation(Account, related_query_name='drivers')
class Car(models.Model):
owner = models.ForeignKey(Driver, on_delete=None)
class Ride(models.Model):
car = models.ForeignKey(Car, on_delete=models.CASCADE)
request = models.OneToOneField(RideRequest, on_delete=models.CASCADE, null=False)
I expect something like this: <QuerySet [<Driver: 1>, <Driver: 2>]>which is Drivers with greater than or equal x Rides
You can count the number of Rides per Driver, and then filter on that number, like:
from django.db.models import Count
Driver.objects.annotate(
nride=Count('car__ride')
).filter(nride__gte=x)
We thus first annotate each Driver object with the Count(..) [Django-doc] of related Rides, next we filter that queryset by specifying that the nrides field annotation should be greater than or equal to x, by using the __gte lookup [Django-doc]. Here x is of course the number of required rides you need to fill in (for example 5).

How to write a complex Mysql Query in Django

I am new to Django and I am working on a small module of a Django application where I need to display the list of people who have common interest as that of any particular User. So Suppose if I am an user I can see the list of people who have similar interests like me.
For this I have 2 models :
models.py
class Entity(models.Model):
name = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.name
class UserLikes(models.Model):
class Meta:
unique_together = (('user', 'entity'),)
user = models.ForeignKey(User)
entity = models.ForeignKey(Entity)
def __str__(self):
return self.user.username + " : " + self.entity.name
So in the Entity Table I store the Entities in which user can be interested Eg : football, Music, Code etc.
and in the UserLikes I store the relation about which user likes which entity.
Now I have a Query to fetch details about which user has maximum interest like any particular user :
SELECT y.user_id, GROUP_CONCAT(y.entity_id) likes, COUNT(*) total
FROM likes_userlikes x
JOIN likes_userlikes y ON y.entity_id = x.entity_id AND y.user_id <> x.user_id
WHERE x.user_id = ?
GROUP BY y.user_id
ORDER BY total desc;
Problem is how do I write this Query using Django Querysets and change it into a function.
# this gives you what are current user's interests
current_user_likes = UserLikes.objects.filter(user__id=user_id) \
.values_list('entity', flat=True).distinct()
# this gives you who are the persons that shares the same interests
user_similar_interests = UserLikes.objects.filter(entity__id__in=current_user_likes) \
.exclude(user__id=user_id) \
.values('user', 'entity').distinct()
# finally the count
user_similar_interests_count = user_similar_interests.count()
Here the user_id is the user's id you want to query for.
One advice though, it's not good practice to use plural form for model names, just use UserLike or better, UserInterest for it. Django would add plural form when it needs to.

manyToManyField question

Hay guys, I'm writing a simple app which logs recipes.
I'm working out my models and have stumbled across a problem
My Dish models needs to have many Ingredients. This is no problem because i would do something like this
ingredients = models.ManyToManyfield(Ingredient)
No problems, my dish now can have many ingrendients.
However, the problem is that the ingredient needs to come in different quantities.
I.E 4 eggs, 7 tablespoons sugar
My Ingredient Model is very simple at the moment
class Ingredient(models.Model):
name = models.TextField(blank=False)
slug = models.SlugField(blank=True)
How would i go about work out this problem? What fields would i need to add, would i need to use a 'through' attribute on my ManyToManyfield to solve this problem?
I think you got the right answer with a "through" table ( http://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany )
Model
class Recipe(models.Model):
name = models.TextField(blank=False)
ingredients = models.ManyToManyField(Ingredient, through='Components')
class Ingredient(models.Model):
name = models.TextField(blank=False)
slug = models.SlugField(blank=True)
class Components(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
quantity = models.DecimalField()
You can put unit of quantity (gram, kilo, tablespoon, etc) on Ingredient level, but I think it is better on Ingredients level (for example you can have 1 recipe with 10 Cl of milk but one other with 1L ... So "different" units for a same ingredient.
Data Creation
By Dish you mean Recipe right ? If you have a look to previous link (http://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany), they give you a good example (based on the beatles).
Basically :
1.Create a Recipe:
cake=Recipe.objects.create(name="Simple Cake")
2.Create several Ingredient (if they doesn't already exist from a previous recipe ;)):
egg = Ingredient.objects.create(name="Egg")
milk = Ingredient.objects.create(name="milk")
3.Create the relationship:
cake_ing1 = Components.objects.create(recipe=cake, ingredient=egg,quantity = 2)
cake_ing2 = Components.objects.create(recipe=cake, ingredient=milk,quantity = 200)
and so on. Plus, I'm now quite sure that unit should go to Components level, with a default unit as "piece" (that would be for yours eggs ...), and would be something like "mL" for milk.
Data Access
In order to get ingredients (Components) of a recipe just do :
cake = Recipe.objects.get(name = "Simple Cake")
components_cake = Components.objects.get(recipe = cake)

Categories