Group Django model objects to a distinct column - python

I have the following Django model
class TestModel(models.Model):
company_name = models.CharField(max_length=100)
nick_name = models.CharField(max_length=100)
created_at = models.DateTimeField()
Now I want to create a query/multiple queries which will group all the TestModel objects to the particular company_name. In a nutshell it will have the following structure.
out - {
"company_name_test_1": [TestModel1, TestModel2, TestModel3],
"company_name_test_2": [TestModel4, TestModel5, TestModel6],
}

companyNames = ["a", "b", "c"]
result = {}
for name in companyNames:
result[name] = list(TestModel.objects.filter(company_name = name))
If you want to save the dictionary as JSON or something similar, the filtered models will need to be serialised first.
Also, depending on the logic of your program, it might be worth having a 'company' model, and having the TestModels as a foreign key to their respective companies.
class Company(models.Model):
name = models.CharField(max_length = 100)
# other fields and functions
class TestModel(models.Model):
company = models.ForeignKey(Company)
nick_name = models.CharField(max_length=100)
created_at = models.DateTimeField()
Then you can just filter by foreign key to get the company's testmodels:
results = {}
for company in Company.objects.all():
results[company.name] = list(company.testmodel_set.all())

Related

django model reference to own attribute

When i create a Django model i want to set the primary_key/ID to a combination of 2 attributes of this Model.
Example model:
class Example(models.Model):
name = models.CharField(max_length=100)
date = models.DateTimeField(default=now)
id = models.CharField(max_length=100, default="{}-{}".format(name, date) primary_key=True)
But he is saving the Model and not the values:
field=models.CharField(default='<django.db.models.fields.related.ForeignKey>-<django.db.models.fields.DateTimeField>', max_length=100, primary_key=True, serialize=False),
I know that name and date are a model in this context, but "self." is not working and i can't find a Django function to get the actual values instead.
It should work like this:
Input:
testmodel = Example(name="foo", date=datetime.now())
testmodel.save()
testmodel-id should look something like this "foo-2020-12-11..."
I don't think you can do that. Because these values are initialized at the making of an object. What you can do is to make your own class method to initialize these values.
class Example(models.Model):
name = models.CharField(max_length=100)
date = models.DateTimeField(default=now)
id = models.CharField(max_length=100, primary_key=True)
# In User class declaration
#classmethod
def create(cls,name, date):
return cls(name=name, date=date, id="{}-{}".format(name,date))
And Use it like this outside the class
p = Example.create("Fred", "11-12-2020")

Django model form field to have a user dropdown list based on a condition

In a Django Modelform (Product_definition), i want to have a dropdown(Merchant name) which will show users only if the their designation in User form is "Merchant".
is it possible that I could get the list of users for the dropdown based on this condition .Please note that i don't require it to be a foreign key as connecting the models is not required.
This is the form which contains the Designation :
from django.contrib.auth.models import User
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete = models.CASCADE)
#extra UserAttribute
MERCHANT = 'MR'
FABRIC = 'FR'
WASHING = 'WS'
PRINT = 'PR'
PLANNER = 'PL'
DESIGNATION_CHOICES =(
(PLANNER,'Planner'),
(MERCHANT,'Merchant'),
(FABRIC,'Fabric'),
(WASHING,'Washing'),
(PRINT,'Printing'),
)
Designation =models.CharField(
max_length = 20,
choices = DESIGNATION_CHOICES,
default= 'PLANNER'
)
def __str__(self):
return self.user.username
and this is the form with Merchant Name where I want the names of all merchants to appear.
class Product_definition(models.Model):
Order_number = models.CharField(max_length=25,unique = True, blank = True, null = True)
style_name = models.CharField(max_length=15, blank = True, null = True)
color = models.CharField(max_length=15, blank = True, null = True)
Order_qty = models.PositiveIntegerField()
SMV = models.FloatField()
MERCHANT = models.ForeignKey(UserProfileInfo,on_delete= models.CASCADE,default='Select')
def __str__(self):
return self.Order_number
I have created a foreign key for now but I don't require it and it doesn't list the names of only the merchant in the drop down.
I think you can do it like this using ModelChoiceField:
class ProductForm(forms.ModelForm): # please use CamelCase when defining Class Names
MERCHANT = forms.ModelChoiceField(queryset=UserProfileInfo.objects.filter(Designation=UserProfileInfo.MARCHENT)) # Please use sname_case when naming attributes
class Meta:
model = Product_definition # Please use CamelCase when defining model class name
fields = '__all__'

Django Count() multiple fields and sort by their sum

I have a model named Topiccenter. and there are many entries in each Topiccenter.
class Topiccenter(models.Model):
name = models.TextField()
class Book(models.Model):
tc = models.ForeignKey(Topiccenter,related_name="tc_book_entries")
name = models.TextField()
class Journal(models.Model):
tc = models.ForeignKey(Topiccenter,related_name="tc_journal_entries")
name = models.TextField()
I want to get topiccenters and sort them by max entries and min entries.
I tried
Topiccenter.objects.annotate(sum_entries=Sum('tc_book_entries','tc_journal_entries')).order_by('-sum_entries')
but this is not working
Try this -
Topiccenter.objects.annotate(
sum_books=Sum('tc_book_entries'),
sum_journals=Sum('tc_journal_entries')
).extra({
'select': {
'sum_entries': 'sum_books + sum_journals'
}
}).order_by('-sum_entries')
This should work.

Include intermediary (through model) in responses in Django Rest Framework

I have a question about dealing with m2m / through models and their presentation in django rest framework. Let's take a classic example:
models.py:
from django.db import models
class Member(models.Model):
name = models.CharField(max_length = 20)
groups = models.ManyToManyField('Group', through = 'Membership')
class Group(models.Model):
name = models.CharField(max_length = 20)
class Membership(models.Model):
member = models.ForeignKey('Member')
group = models.ForeignKey('Group')
join_date = models.DateTimeField()
serializers.py:
imports...
class MemberSerializer(ModelSerializer):
class Meta:
model = Member
class GroupSerializer(ModelSerializer):
class Meta:
model = Group
views.py:
imports...
class MemberViewSet(ModelViewSet):
queryset = Member.objects.all()
serializer_class = MemberSerializer
class GroupViewSet(ModelViewSet):
queryset = Group.objects.all()
serializer_class = GroupSerializer
When GETing an instance of Member, I successfully receive all of the member's fields and also its groups - however I only get the groups' details, without extra details that comes from the Membership model.
In other words I expect to receive:
{
'id' : 2,
'name' : 'some member',
'groups' : [
{
'id' : 55,
'name' : 'group 1'
'join_date' : 34151564
},
{
'id' : 56,
'name' : 'group 2'
'join_date' : 11200299
}
]
}
Note the join_date.
I have tried oh so many solutions, including of course Django Rest-Framework official page about it and no one seems to give a proper plain answer about it - what do I need to do to include these extra fields? I found it more straight-forward with django-tastypie but had some other problems and prefer rest-framework.
How about.....
On your MemberSerializer, define a field on it like:
groups = MembershipSerializer(source='membership_set', many=True)
and then on your membership serializer you can create this:
class MembershipSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.Field(source='group.id')
name = serializers.Field(source='group.name')
class Meta:
model = Membership
fields = ('id', 'name', 'join_date', )
That has the overall effect of creating a serialized value, groups, that has as its source the membership you want, and then it uses a custom serializer to pull out the bits you want to display.
EDIT: as commented by #bryanph, serializers.field was renamed to serializers.ReadOnlyField in DRF 3.0, so this should read:
class MembershipSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField(source='group.id')
name = serializers.ReadOnlyField(source='group.name')
class Meta:
model = Membership
fields = ('id', 'name', 'join_date', )
for any modern implementations
I was facing this problem and my solution (using DRF 3.6) was to use SerializerMethodField on the object and explicitly query the Membership table like so:
class MembershipSerializer(serializers.ModelSerializer):
"""Used as a nested serializer by MemberSerializer"""
class Meta:
model = Membership
fields = ('id','group','join_date')
class MemberSerializer(serializers.ModelSerializer):
groups = serializers.SerializerMethodField()
class Meta:
model = Member
fields = ('id','name','groups')
def get_groups(self, obj):
"obj is a Member instance. Returns list of dicts"""
qset = Membership.objects.filter(member=obj)
return [MembershipSerializer(m).data for m in qset]
This will return a list of dicts for the groups key where each dict is serialized from the MembershipSerializer. To make it writable, you can define your own create/update method inside the MemberSerializer where you iterate over the input data and explicitly create or update Membership model instances.
I just had the same problem and I ended it up solving it with an annotation on the group queryset.
from django.db.models import F
class MemberSerializer(ModelSerializer):
groups = serializers.SerializerMethodField()
class Meta:
model = Member
def get_groups(self, instance):
groups = instance.groups.all().annotate(join_date=F(membership__join_date))
return GroupSerializer(groups, many=True).data
class GroupSerializer(ModelSerializer):
join_date = serializers.CharField(required=False) # so the serializer still works without annotation
class Meta:
model = Group
fields = ..., 'join_date']
NOTE: As a Software Engineer, I love to use Architectures and I have deeply worked on Layered Approach for Development so I am gonna be Answering it with Respect to Tiers.
As i understood the Issue, Here's the Solution
models.py
class Member(models.Model):
member_id = models.AutoField(primary_key=True)
member_name = models.CharField(max_length =
class Group(models.Model):
group_id = models.AutoField(primary_key=True)
group_name = models.CharField(max_length = 20)
fk_member_id = models.ForeignKey('Member', models.DO_NOTHING,
db_column='fk_member_id', blank=True, null=True)
class Membership(models.Model):
membershipid = models.AutoField(primary_key=True)
fk_group_id = models.ForeignKey('Group', models.DO_NOTHING,
db_column='fk_member_id', blank=True, null=True)
join_date = models.DateTimeField()
serializers.py
import serializer
class AllSerializer(serializer.Serializer):
group_id = serializer.IntegerField()
group_name = serializer.CharField(max_length = 20)
join_date = serializer.DateTimeField()
CustomModels.py
imports...
class AllDataModel():
group_id = ""
group_name = ""
join_date = ""
BusinessLogic.py
imports ....
class getdata(memberid):
alldataDict = {}
dto = []
Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
alldataDict["MemberId"] = Member.member_id
alldataDict["MemberName"] = Member.member_name
Groups = models.Group.objects.filter(fk_member_id=Member)
for item in Groups:
Custommodel = CustomModels.AllDataModel()
Custommodel.group_id = item.group_id
Custommodel.group_name = item.group_name
Membership = models.Membership.objects.get(fk_group_id=item.group_id)
Custommodel.join_date = Membership.join_date
dto.append(Custommodel)
serializer = AllSerializer(dto,many=True)
alldataDict.update(serializer.data)
return alldataDict
You would technically, have to pass the Request to DataAccessLayer which would return the Filtered Objects from Data Access Layer but as I have to Answer the Question in a Fast Manner so i adjusted the Code in Business Logic Layer!

GeoDjango: Sort by country of the geolocation

Hi Stackoverflow people,
I have a model which contains projects with the corresponding geolocation:
class Project(models.Model):
name = models.CharField(_('Project Name'), max_length=100, null=True, blank=True)
geolocation = models.PointField(_('Project Location'))
...
In addition, another model is representing a shapefile with the country borders:
class WorldBorder(models.Model):
name = models.CharField(max_length=50)
mpoly = models.MultiPolygonField()
objects = models.GeoManager()
class Meta:
ordering = ('name',)
def __unicode__(self):
return self.name
How can I do a query on Project and order the results by the country name of the geolocation?
A query like
d = Project.objects.all().order_by(geolocation__name)
does not work since geolocation is not a Foreignkey. Do I really have loop through all projects and determine the country manually like in my example below?
projects = Project.objects.all()
result = []
for project in projects
country = WorldBorder.objects.filter(mpoly__contains = project.geolocation)
foo = [project.name, country]
result.append(foo)
# now sort the list according to the country
result = sorted(result, key=itemgetter(1))
There should be a more professional and elegant solution? Any suggestions from the experienced Python people? Can I use joins for that purpose?
Thank you for your suggestions!

Categories