How to rearrange priority field for a django model? - django

I have a Model with a priority field of type postitive integer. This field is unique and allows me to manage the priority of objects.
For example, I want the most important object to have priority one, the second most important to have priority two, etc...
Example:
[
{ "name": "object82",
"priority": 1
}
{ "name": "object54",
"priority": 2
}
{ "name": "object12",
"priority": 3
}
]
class MyObject(models.Model):
name = models.CharField(_("name"), max_length=255)
priority = models.PositiveSmallIntegerField(_("priority"), unique=True)
I want to override the object serializer so that if I add a new object with an existing priority, it unpacks the existing objects. (same thing for the path of an existing object)
For example if I take the example above and add:
{ "name": "object22",
"priority": 2
}
I want the following result:
[
{ "name": "object82",
"priority": 1 // the priority didn't changed
}
{ "name": "object22", // my new object
"priority": 2
}
{ "name": "object54",
"priority": 3 // the priority had changed
}
{ "name": "object12", // the priority had changed
"priority": 4
}
]
I think I have to check first if an object with the same priority exists in the database or not.
If not => I save as is
If yes, I have to change the priority of some objects before add the new object.
How to do this ?
Maybe something like:
class MyObjectSerializer(serializers.ModelSerializer):
class Meta:
model = MyObject
fields = '__all__'
def update(self, instance, validated_data):
target_priority = validated_data.get('priority')
if MyObject.objects.filter(target_priority).exists():
existing_priorities = MyObject.objects.filter(priority__gte=target_priority)
for existing_priority in existing_priorities:
existing_priority.priority += 1
existing_priority.save(update_fields=['priority'])
instance.priority = target_priority
instance.save(update_fields=['priority'])

I was facing a similar problem, and what I have done is that I have a model form and I'm doing the validation in clean function
def clean(self):
cleaned_data = super().clean()
priority = cleaned_data.get('priority')
task = Task.objects.filter(priority__exact=priority)
while task.exists():
prev_task_id = task[0].id
task.update(priority=priority+1)
priority += 1
task = Task.objects.filter(priority__exact=priority).exclude(pk=prev_task_id)
return cleaned_data
I have used the prev_task_id variable for excluding the model that is just got updated. For e.g. let's say we have data
{
title: 'first one',
priority: 3
},
{
title: 'second one',
priority: 4
}
So now if I got priority 3 and after updating it we will have two tasks with priority 4 so we have to exclude the previous task i.e. 'first one'. We have to only update the second task in next iteration
PS:- This code is written assuming that in the database no duplicate priority exists.

Related

Better way to do the django api response

I have tried to create a model to represent a situation like this.
each question row can have a multiple question column. there are multiple types of question column
class QuestionRow(models.Model):
report_question = models.CharField(max_length=200)
report_group = models.CharField(max_length=20)
class QuestionColumn(models.Model):
header_text = models.CharField(max_length=100)
data_type = models.CharField(max_length=10)
class QuestionItem(models.Model):
column = models.ForeignKey(QuestionColumn)
row = models.ForeignKey(QuestionRow)
My objective is to find the optimized way to query and return the response.
where each question row in question item may or may not have multiple question columns. thinking of a way to do this using django annotate, aggregate
[{
"report_group": 1,
"question_row": "1a. Alarm system not active or not sufficient?",
"question_columns" : [
{
"header_text": "Yes/No",
"data_type": "Bool"
},
{
"header_text": "Risk Score",
"data_type": ""
}
]
},
{
"report_group": 1,
"question_row": "1b. Notification system not active or inactive?",
"question_columns" : [
{
"header_text": "Yes/No",
"data_type": "Bool"
},
{
"header_text": "Risk Score",
"data_type": ""
}
]
}]

Django Rest Framework: Get unique list of values from nested structure

I want to be able to return a list of strings from a deeply nested structure of data. In this scenario, I have a API that manages a chain of bookstores with many locations in different regions.
Currently, I have an API endpoint that takes a region's ID and returns a nested JSON structure of details about the region, the individual bookstores, and the books that can be found in each store.
{
"region": [
{
"store": [
{
"book": {
"name": "Foo"
}
},
{
"book": {
"name": "Bar"
}
},
{
"book": {
"name": "Baz"
}
}
],
},
{
"store": [
{
"book": {
"name": "Foo"
}
},
{
"book": {
"name": "Bar"
}
}
],
},
{
"store": [
{
"book": {
"name": "Foo"
}
},
{
"book": {
"name": "Baz"
}
},
{
"book": {
"name": "Qux"
}
}
]
}
]
}
My models look like the following. I am aware these models don't make the most sense for this contrived example, but it does reflect my real world code:
class Book(TimeStampedModel):
name = models.CharField(default="", max_length=512)
class Bookstore(TimeStampedModel):
value = models.CharField(default="", max_length=1024)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
class Region(TimeStampedModel):
stores = models.ManyToManyField(Bookstore)
class BookstoreChain(TimeStampedModel):
regions = models.ManyToManyField(Region)
The serializers I created for the above response look like:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = "__all__"
class BookstoreSerializer(serializers.ModelSerializer):
books = BookSerializer()
class Meta:
model = Bookstore
fields = "__all__"
class RegionSerializer(serializers.ModelSerializer):
stores = BookstoreSerializer(many=True)
class Meta:
model = Region
fields = "__all__"
class BookstoreChainSerializer(serializers.ModelSerializer):
regions = RegionSerializer(many=True)
class Meta:
model = BookstoreChain
fields = "__all__"
I'm not sure what my view or serializer for this solution need to look like. I'm more familiar with writing raw SQL or using an ORM/Linq to get a set of results.
While the above response is certainty useful, what I really want is an API endpoint to return a unique list of book names that can be found in a given region (Foo, Bar, Baz, Qux). I would hope my response to look like:
{
"books": [
"Foo",
"Bar",
"Baz",
"Qux"
]
}
My feeble attempt so far has a urls.py with the following path:
path("api/regions/<int:pk>/uniqueBooks/", views.UniqueBooksForRegionView.as_view(), name="uniqueBooksForRegion")
My views.py looks like:
class UniqueBooksForRegionView(generics.RetrieveAPIView):
queryset = Regions.objects.all()
serializer_class = ???
So you start from region you have to get the stores, so you can filter the books in the stores, here is a solution which will work.
Note:
Avoid using .get() in *APIView because it will trigger an error if the request does not have the ID, you can use get_object_or_404(), but then you cannot log your error in Sentry.
To get an element from an *APIView, use filter().
import logging as L
class UniqueBooksForRegionView(generics.RetrieveAPIView):
lookup_field = 'pk'
def get(self, *args, **kwargs)
regions = Region.objects.filter(pk=self.kwargs[self.lookup_field])
if regions.exists():
region = regions.first()
stores_qs = region.stores.all()
books_qs = Book.objects.filter(store__in=stores_qs).distinct()
# use your book serializer
serializer = BookSerializer(books_qs, many=True)
return Response(serializer.data, HTTP_200_OK)
else:
L.error(f'Region with id {self.kwargs[self.lookup_field]} not found.')
return Response({'detail':f'Region with id {self.kwargs[self.lookup_field]} not found.'}, HTTP_404_NOT_FOUND)
Note
Here is the flow, the code may need some tweaks, but I hope it helps you understand the flow

Django annotate several same objects in QuerySet by different related object

I got:
# models
class Building(models.Model):
...
class Flat(models.Model):
building = models.ForeignKey(Building)
class Profile(models.Model):
flats = models.ManyToManyField(Flat)
# logic
building = Building.objects.create()
flat_1 = Flat.objects.create(building=building)
flat_2 = Flat.objects.create(building=building)
profile = Profile.objects.create()
profile.flats.add(flat_1)
profile.flats.add(flat_2)
profiles = Profile.objects.filter(flats__building=building)
I got in profiles 2 same profiles. How i can annotate each of them by different flat like this: profiles.first().flat == flat_1 and profiles.last().flat == flat_2?
Maybe Subquery() but how?
UPD I need this in some DRF list view. Output in JSON must be something like:
[
{
"profile_id": 1,
"flat_id": 2
},
{
"profile_id": 1,
"flat_id": 3
}
]
To obtain that output, you could do:
data = Profile.objects.all().values('flats', 'id')
return Response(data=data)
in your DRF view.
You don't have to profile instances ...
I wrote the code for your exact needs at the end, but first wrote a couple of things that might be of interest.
In your code sample, you've created only one profile, I'm sure you are not getting 2 instances of Profile that are equals but only one.
The thing is if you have a QuerySet with only one entry, then:
profiles.first() == profiles.last() # True
since profile.first() and profiles.last() are the same instance.
You should try creating 2 Profile instances:
building = Building.objects.create()
flat_1 = Flat.objects.create(building=building)
flat_2 = Flat.objects.create(building=building)
profile_1 = Profile.objects.create() # You coud/should use bulk_create here.
profile_2 = Profile.objects.create()
profile_1.flats.add(flat_1)
profile_2.flats.add(flat_2)
Then
profiles = Profile.objects.filter(flats__building=building)
will return two different profile objects.
On the other hand, obtaining the JSON like you want ...
Following the example, you posted, filter flats by profile and get the values (this also works if you have more that one profile).
Flat.objects.filter(profile=profile_1).values('profile__id', 'id')
This will return something like ("id" stands for flats ids):
[
{
"profile__id": 1,
"id": 1
},
{
"profile__id": 1,
"id": 3
}
]
If you do not filter by profile (and you have more than one) you could get something like:
[
{
"profile__id": 1,
"id": 1
},
{
"profile__id": 2,
"id": 3
},
{
"profile__id": 2,
"id": 4
},
...
]
Annotating to get the EXACT json you want:
Filter as shown previously annotate, and get desired values:
Flat.objects.filter(profile=profile_1).annotate(
flat_id=F('id')
).annotate(
profile_id=F('profile__id')
).values(
'profile_id', 'flat_id'
)
will give exactly what you want:
[
{
"profile_id": 1,
"flat_id": 2
},
{
"profile_id": 1,
"flat_id": 3
}
]
You can do that with the right serializer and the right annotation:
The serializer:
class FlatSerializer(serializers.ModelSerializer):
class Meta:
model = Flat
fields = ('flat_id', 'building_id')
flat_id = serializers.CharField(read_only=True)
Then I would simply query Flats rather than profiles and serialize:
flats = Flat.objects \
.annotate(flat_id=F('id')) \
.filter(building=building)
serialized = FlatSerializer(flats, many=True)
print(serialized.data) # [ { flat_id: 1, building_id: 1 }, { flat_id: 2, building_id: 1 } ]
Let me know if that works for you

Django graphql graphene removing redundant queries from return value

The last few days I have read up on so much graphql that I can't see the trees from the forest anymore.
The results that this person got in the beginning is almost exactly what I want (his problem, not his solution), but it seems that a lot of the code is deprecated and I can't seem to get it working: link
I have a bunch of containers that I return. All the containers have the amounts for every day in them. I only want to return the amounts of a certain day.
At the moment, I do return these results (day), but all the other results (days) also return with a Null value.
Current behavior:
{
"data": {
"listProductcontainers": [
{
"id": "1",
"productid": {
"productid": "CBG2",
"processedstockamountsSet": [
{
"timeStampID": {
"id": "2"
},
"id": "77745",
"prodName": {
"productid": "CBG2"
}
},
{
"timeStampID": null, <--------
"id": "89645",
"prodName": {
"productid": "CBG2"
}
},
{
"timeStampID": null, <--------
"id": "89848",
"prodName": {
"productid": "CBG2"
}
},
// ...
Requested behavior: (All values with 'Null' should not return)
{
"data": {
"listProductcontainers": [
{
"id": "1",
"productid": {
"productid": "CBG2",
"processedstockamountsSet": [
{
"timeStampID": {
"id": "2"
}
My query that I am running looks like this:
query{
listProductcontainers{
id
productid{
productid
processedstockamountsSet{
timeStampID(id:2){
id
}
id
prodName{
productid
}
}
}
}
}
Here are the relevant code for the results:
class TimeStampType(DjangoObjectType):
class Meta:
model = TimeStamp
class ProcessedStockAmountsType(DjangoObjectType):
timeStampID = graphene.Field(TimeStampType, id=graphene.Int())
class Meta:
model = ProcessedStockAmounts
def resolve_timeStampID(self, info, **kwargs):
id = kwargs.get('id')
if self.timeStampID.id == id:
return self.timeStampID
class ProductcontainersType(DjangoObjectType):
class Meta:
model = Productcontainers
class ProductlistType(DjangoObjectType):
class Meta:
model = Productlist
class Query(graphene.ObjectType):
list_productcontainers = graphene.List(ProductcontainersType)
def resolve_list_productcontainers(self, context, **kwargs):
return Productcontainers.objects.all()
I have read almost everything in graphene by now, but if you even have a link that mirrors what I want to do I would really appreciate it.
My final option is to make two calls where I get all the container ids, and another call where I get all the amounts (with container id) for a certain date, and with 2 for loops I just add the amounts into the corresponding container... :(

Django: defining a generic manager in an Abstract model

Using the following code and defining suitable NATURAL_KEY per every class fails (instead of defining different mangers in every class, which replicate the same code):
class NexchangeManager(models.Manager):
def get_by_natural_key(self, param):
if param == "*":
return self.all()
lookup = {self.NATURAL_KEY: param}
return self.get(**lookup)
class NexchangeModel(models.Model):
class Meta:
abstract = True
objects = NexchangeManager()
Djagno complains about fields replication, although NexhcnageModel is an Abstract model.
Should I use a mixin instead?
error:
django.core.serializers.base.DeserializationError: Problem installing fixture '/Users/beoleg/dev/nexchange/core/fixtures/pairs_cross.json': 'NexchangeManager' object has no attribute 'NATURAL_KEY': (core.pair:pk=1) field_value was '['LTC']'
The purpose of this, a bit overcomplicated code at first glance is, to have something like this in my fixtures:
[
{
"model": "payments.paymentpreference",
"pk": 8,
"fields": {
"user": ["onit"],
"identifier": "paypal#nexchange.co.uk",
"payment_method": 12,
"comment": "Please send the funds as a personal payment (this is a precaution to prevent charge backs, payments for goods and services will be automatically declined)",
"currency": [
["*"]
],
"created_on":"2016-11-01T17:41:28+00:00",
"modified_on":"2016-11-01T17:41:28+00:00"
}
}
]
Instaed of:
[
{
"model": "payments.paymentpreference",
"pk": 8,
"fields": {
"user": ["onit"],
"identifier": "paypal#nexchange.co.uk",
"payment_method": 12,
"comment": "Please send the funds as a personal payment (this is a precaution to prevent charge backs, payments for goods and services will be automatically declined)",
"currency": [
["USD"],
["RUB"],
["EUR"],
["GBP"],
["JPY"],
["HRK"],
["CHF"],
["PLN"],
["RON"],
["BGN"],
["CZK"],
["AUD"],
["CAD"],
["NOK"],
["SEK"],
["DKK"],
["HUF"],
["TRY"],
["ZAR"],
["NZD"],
["BRL"],
["IDR"],
["ILS"],
["INR"],
["KRW"],
["MXN"],
["MYR"],
["PHP"],
["THB"]
],
"created_on":"2016-11-01T17:41:28+00:00",
"modified_on":"2016-11-01T17:41:28+00:00"
}
}
]
I don't understand the way you write your manager. If the NATURAL_KEY is an attribute of each model, I would write:
class NexchangeManager(models.Manager):
def get_by_natural_key(self, param):
qs = self.get_queryset()
if param == "*":
return qs.all()
lookup = {qs.model.NATURAL_KEY: param}
return qs.filter(**lookup)
Note: this answer is based on albars answer, but improved for allowing pk params.
manager:
class NexchangeManager(models.Manager):
def get_by_natural_key(self, param):
qs = self.get_queryset()
if param == "*":
return self.all()
lookup = {qs.model.NATURAL_KEY: param}
return self.get(**lookup)
Generic Model class:
class NexchangeModel(models.Model):
class Meta:
abstract = True
objects = NexchangeManager()