Database methods to get a list of non duplicated instances? - django

In my models I have a class like the following:
class Contact(models.Model):
group = models.CharField(max_length=200, blank=True)
name = models.CharField(max_length=100)
I'd like to find the better way of getting a list of all the groups. So far
I have two solutions:
groups=[]
for contact in Contact.objects.all():
if not contact.group in groups:
groups.append(contact.group)
and the second one:
groups=set(contact.group for contact in Contact.objects.all())
I think that the second one is much better because it uses generators, but I'd like to know if there is some database method like filter, exclude , etc that could allow me to do this.
The point of doing this is to optimize when an user has a lot of contacts but just a few groups. (In that case maybe making a class group would be better, but I'd really like to avoid that)

Best way is to use distinct
groups = Contact.objects.values_list('group', flat=True).distinct()

Related

django subquery with a join in it

I've got django 1.8.5 and Python 3.4.3, and trying to create a subquery that constrains my main data set - but the subquery itself (I think) needs a join in it. Or maybe there is a better way to do it.
Here's a trimmed down set of models:
class Lot(models.Model):
lot_id = models.CharField(max_length=200, unique=True)
class Lot_Country(models.Model):
lot = models.ForeignKey(Lot)
country = CountryField()
class Discrete(models.Model):
discrete_id = models.CharField(max_length=200, unique=True)
master_id = models.ForeignKey(Inventory_Master)
location = models.ForeignKey(Location)
lot = models.ForeignKey(Lot)
I am filtering on various attributes of Discrete (which is discrete supply) and I want to go "up" through Lot, over the Lot_Country, meaning "I only want to get rows from Discrete if the Lot associated with that row has an entry in Lot_Country for my appropriate country (let's say US.)
I've tried something like this:
oklots=list(Lot_Country.objects.filter(country='US'))
But, first of all that gives me the str back, which I don't really want (and changed it to be lot_id, but that's a hack.)
What's the best way to constrain Discrete through Lot and over to Lot_Country? In SQL I would just join in the subquery (or even in the main query - maybe that's what I need? I guess I don't know how to join up to a parent then down into that parent's other child...)
Thanks in advance for your help.
I'm not sure what you mean by "it gives me the str back"... Lot_Country.objects.filter(country='US') will return a queryset. Of course if you print it in your console, you will see a string.
I also think your models need refactoring. The way you have currently defined it, you can associate multiple Lot_Countrys with one Lot, and a country can only be associated with one lot.
If I understand your general model correctly that isn't what you want - you want to associate multiple Lots with one Lot_Country. To do that you need to reverse your foreign key relationship (i.e., put it inside the Lot).
Then, for fetching all the Discrete lots that are in a given country, you would do:
discretes_in_us = Discrete.objects.filter(lot__lot_country__country='US')
Which will give you a queryset of all Discretes whose Lot is in the US.

Deciding how to model this data in django

Imagine it is for translating vocabulary to another language. I'm only dealing with a limited number of words (~2000).
Language1 and Language2 (which will have a different ~2000 words), each might have multiple words equivalents from the other language which may or may not be on the list of ~2000 words of the other language.
Using a many-to-many relationship initially appealed to me, but I can't quite see through the mist to see what would work best.
My other thought was just making a json dump for each word. Something like....
{1: {'Lang1': [word1, word2], 'Lang2': [word1, word2]}}
but I am not sure if that is too smart to manage everything like that, it would be cumbersome to do from the admin section (because I think I would be editing a long line of text that is a json object) and it doesn't take advantage of much.
Maybe there is another way that I havent thought of?
Given my scenario, how would you go about defining this?
class Language(models.Model):
name = models.CharField(max_length=100)
class Word(models.Model):
name = models.CharField(max_length=100)
language = models.ForeignKey(Language, related_name='language_words')
#...
class Translation(models.Model):
word = models.ForeignKey(Word, related_name='word_translations')
translation = models.CharField(max_length=100)
from_language = models.ForeignKey(Language, related_name='language_translations')
in_language = models.CharField(max_length=100)
# stage performances
english_language = Language(name='english')
english_language.save()
word = english_language.language_words.create(name='Flower')
german_translation = word.word_translations.create(translation='Blumen',
from_language=english_language,
in_language='German')
word # 'Flower'
german_translation # 'Blumen'
might not be optimal yet, i am in the train right now, but this can be a good way to start hopefully.
then if you register these models into admin, you can easily manage (add/delete) translations..

Groups per object using Django and django-guardian object permissions

I'm currently creating a structure where I have employees which belong to a company.
Within this company I need to be able to create several groups. Ranks if you will. You could assign less permissions to lower ranks and more permissions to higher ranks.
I want to go for object level permissions and I noticed the django-guardian project gave me exactly what I needed. It works with the native User and Group objects so I'm now trying to find a way to implement the native group object in a company object.
Problems I face is that name in group is unique. So if 2 companies add the same group, errors will occur.
I found an implementation that works in a way but seems quite 'hacky' to me. In my company I declared a group variable that references Group:
class Company(models.Model):
...
groups = models.ManyToManyField(Group, through='CompanyRole')
CompanyRole basically houses the group name and a reference to company and group
class CompanyRole(models.Model):
group = models.ForeignKey(Group)
company = models.ForeignKey(Company)
real_name = models.CharField(max_length=60, verbose_name=_('Real name'))
objects = CompanyGroupManager()
I created a custom manager with a convenient method to add a new 'company group'
class CompanyGroupManager(models.Manager):
def create_group(self, company, group_name):
un_group_name = str(company.id) + '#' + group_name
group = Group.objects.create(name=un_group_name)
company_group = self.model(
real_name=group_name,
company=company,
group=group
)
company_group.save(using=self._db)
return company_group
Here's the part I don't really feel confortable about. In order to change the problem with the unique name on the Group model I used a combination of the company id, a hash sign and the actual group name to avoid clashes.
Now my question is: are there better methods in my scenario, am I missing something or is this a good way of accomplishing what I need?
Unfortunately there is no way of getting around the unique requirement, that is because this field is used as the id:
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.unique
Your options are the following:
1) Mocking the model.
You would basically just create a new Group model that doesn't have the unique requirement. The downside here is that you'd need to use it everywhere, so if this requires updating 3rd party apps, it might not be worth it.
2) make the name you unique. (As you did)
Make sure that you document your convention well, so that all future coders will know what they are looking at.Something like "company name"#"group name" could make more intuitive sense than an id. If the a hash might appear in either then use a more certain delimiter ("__" is a relatively common way of connecting related concepts in django, I might go for this).
I would recommend that you add the following to make it easy for you to access the name.
def get_name(self):
# Explain how you get the group name from your uniqueified name
return self.name.split('#')[1]
Group.add_to_class('get_name', get_name)
When you access your group's name in your app, just do:
my_group.get_name()
You might also want to put the generating the uniqueified name into an overridden version of the save(). This would give you nicer split between model and view...

Django INNER JOIN by field

Say I have a model that is
class Bottles(models.Model)
BottleCode = models.IntegerField()
class Labels(models.Model)
LabelCode = models.IntegerField()
How do I get a queryset of Bottles where the BottleCode and LabelCode are equal? (i.e. Bottles and Labels with no common Code are excluded)
It can be achieved via extra():
Bottles.objects.extra(where=["Bottles.BottleCode in (select Labels.LabelCode from Labels)"])
You may also need to add an app name prefix to the table names, e.g. app_bottles instead of bottles.
Though #danihp has a point here, if you would often encounter queries like these, when you are trying to relate unrelated models - you should probably think about changing your model design.

Django Multiple Levels Choices for Field

I have a model that I would like to use the choices= option for, but three levels deep.
class Doctor(models.Model):
...
zipcode = models.CharField(max_length=10, choices=AREAS, null=True, blank=True)
Within the "zipcode" dropdown in the admin, I would like the hierarchy to be:
Bronx
--Kingsbridge
----10463
----10471
--Fordham
----10458
----10467
----10468
Brooklyn
--Borough Park
----11204
etc.
Then, if I choose zip code 10463, the Doctor object will be associated with the Kingsbridge area in the Bronx. I'm trying this a variety of different ways. The closest I've come is using this:
AREAS = (
('Bronx', (('Kingsbridge', ('10463', '10463'),),)),
...
)
Unfortunately, that gives me this hierarchy:
Bronx
--('10463', '10463')
which is weird and not helpful. Can anybody see where I'm going wrong? Is this hierarchy possible? Would it be smarter to just create another table in the app called Areas and use a manytomany field? The more I think about it, the more I think I have to use a manytomany field. Thanks in advance
As per https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.Field.choices,
it seems to be only 2 level hierarchy is supported. If you need more than 2 levels, you need to use either custom widgets (or) multiple fields with Foreign Key relationships.