django bulk_create with Null value - django

models.py
class control(models.Model):
amount = models.IntegerField()
driver = models.ForeignKey(driver, on_delete=models.CASCADE, null=True, blank=True)
views.py
controlValues = [
(1,1,1),
(2,8,None)
]
control.objects.bulk_create([
control(
id = i[0],
amount = i[1],
driver = driver(id = i[2])
) for i in controlValues], ignore_conflicts=True
)
I got error:
bulk_create() prohibited to prevent data loss due to unsaved related object 'driver'.
How can I set Null for driver? I'm using mysql.

If the value is None, you should not construct a driver model with that object, but thus work with:
control.objects.bulk_create([
control(
id=id,
amount=amount,
driver=driver(id=dr_id) if dr_id is not None else None
) for id, amount, dr_id in controlValues],
ignore_conflicts=True
)
or a lot simpler:
control.objects.bulk_create([
control(
id=id,
amount=amount,
driver_id=dr_id
) for id, amount, dr_id in controlValues],
ignore_conflicts=True
)
Note: Models in Django are written in PascalCase, not snake_case,
so you might want to rename the model from driver to Driver.

Related

Django get_FOO_Display() for range list choices

STATUS_APPROVE, STATUS_REJECT, STATUS_PROC, STATUS_PEND, STATUS_PAID = range(5)
STATUS_CHOICES = (
(STATUS_APPROVE, 'Approved'),
(STATUS_REJECT, 'Rejected'),
(STATUS_PROC, 'Processing'),
(STATUS_PEND, 'Pending'),
(STATUS_PAID, 'Paid'),
)
class ExpenseClaim(TimeStampedModel):
status = models.PositiveIntegerField(default=STATUS_PEND,
choices=STATUS_CHOICES, blank=False)
class MileageClaimSerializer(serializers.ModelSerializer):
status = serializers.ChoiceField(choices=STATUS_CHOICES)
I cannot work out a way to get foo display for a choice list with range. So the range keys are used to store the point in list. I need the serializer to return the word and not the key.
if this serializer is readonly you can use the source attribute inside a CharField
status = serializers.CharField(source='get_status_display')
https://www.django-rest-framework.org/api-guide/fields/#source

Filtering X most recent entries in each category of queryset

Question is regarding filtering X most recent entries in each category of queryset.
Goal is like this:
I have a incoming queryset based on the following model.
class UserStatusChoices(models.TextChoices):
CREATOR = 'CREATOR'
SLAVE = 'SLAVE'
MASTER = 'MASTER'
FRIEND = 'FRIEND'
ADMIN = 'ADMIN'
LEGACY = 'LEGACY'
class OperationTypeChoices(models.TextChoices):
CREATE = 'CREATE'
UPDATE = 'UPDATE'
DELETE = 'DELETE'
class EntriesChangeLog(models.Model):
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
)
object_id = models.PositiveIntegerField(
)
content_object = GenericForeignKey(
'content_type',
'object_id',
)
user = models.ForeignKey(
get_user_model(),
verbose_name='user',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='access_logs',
)
access_time = models.DateTimeField(
verbose_name='access_time',
auto_now_add=True,
)
as_who = models.CharField(
verbose_name='Status of the accessed user.',
choices=UserStatusChoices.choices,
max_length=7,
)
operation_type = models.CharField(
verbose_name='Type of the access operation.',
choices=OperationTypeChoices.choices,
max_length=6,
)
And I need to filter this incoming queryset in a such way to keep only 4 most recent objects (defined by access_time field) in each category. Categories are defined by ‘content_type_id’ field and there are 3 possible options.
Lets call it ‘option1’, ‘option2’ and ‘option3’
This incoming queryset might contain different amount of objects of 1,2 or all 3 categories. This is can’t be predicted beforehand.
DISTINCT is not possible to use as after filtering operation this queryset might be ordered.
I managed to get 1 most recent object in a following way:
# get one most recent operation in each category
last_operation_time = Subquery(
EntriesChangeLog.objects.filter(user=OuterRef('user')).values('content_type_id').
annotate(last_access_time=Max(‘access_time’)).values_list('last_access_time', flat=True)
)
queryset.filter(access_time__in=last_operation_time)
But I have a hard time to figure out how to get last 4 most recent objects instead of last one.
This is needed for Django-Filter and need to be done in one query.
DB-Postgres 12
Do you have any ideas how to do such filtration?
Thanks...
pk_to_rank = queryset.annotate(rank=Window(
expression=DenseRank(),
partition_by=('content_type_id',),
order_by=F('access_time').desc(),
)).values_list('pk', 'rank', named=True)
pks_list = sorted(log.pk for log in pk_to_rank if log.rank <= value)
return queryset.filter(pk__in=pks_list)
Managed to do it only this way by spliting queryset in 2 parts. Option with 3 unions is also possible but what if we have 800 options instead 3 - make 800 unions()??? ges not...

how to get the latest in django model

In this model:
class Rank(models.Model):
User = models.ForeignKey(User)
Rank = models.ForeignKey(RankStructure)
date_promoted = models.DateField()
def __str__(self):
return self.Rank.Name.order_by('promotion__date_promoted').latest()
I'm getting the error:
Exception Value:
'str' object has no attribute 'order_by'
I want the latest Rank as default. How do I set this?
Thanks.
Update #1
Added Rank Structure
class RankStructure(models.Model):
RankID = models.CharField(max_length=4)
SName = models.CharField(max_length=5)
Name = models.CharField(max_length=125)
LongName = models.CharField(max_length=512)
GENRE_CHOICES = (
('TOS', 'The Original Series'),
('TMP', 'The Motion Picture'),
('TNG', 'The Next Generation'),
('DS9', 'Deep Space Nine'),
('VOY', 'VOYAGER'),
('FUT', 'FUTURE'),
('KTM', 'KELVIN TIMELINE')
)
Genre = models.CharField(max_length=3, choices=GENRE_CHOICES)
SPECIALTY_OPTIONS = (
('CMD', 'Command'),
('OPS', 'Operations'),
('SCI', 'Science'),
('MED', 'Medical'),
('ENG', 'Engineering'),
('MAR', 'Marine'),
('FLT', 'Flight Officer'),
)
Specialty = models.CharField(max_length=25, choices=SPECIALTY_OPTIONS)
image = models.FileField(upload_to=image_upload_handler, blank=True)
This is the Rank_structure referenced by Rank in Class Rank.
THe User Foreign key goes to the standard User table.
The reason that you’re getting an error is because self.Rank.Name is not a ModelManager on which you can call order_by. You’ll need an objects in there somewhere if you want to call order_by. We can’t help you with the django formatting for the query you want unless you also post the model definitions as requested by several commenters. That said, I suspect that what you want is something like:
def __str__(self):
return self.objects.filter(Rank_id=self.Rank_id).order_by('date_promoted').latest().User.Name

Django selecting a count

I'm fairly new to Django and I want to get the total clients a user has (my users will be selling something through my website) so I have a table called orders where I keep the user_id of the user who purchased and the product_id being purchased. Each product id is related to a product that has a user (the one I'm doing the query for):
select COUNT(distinct(o.user_id)) as total_clients from `order` o
inner join product p on p.id=o.product_id where p.user_id=32;
User id 32 is logged in and I want to show him how many clients purchased his products.
I want to do this in a get instead of filter as it makes more sense.
Here's what my logic tells me to write:
clients = Order.objects.get(
status = Order.COMPLETED,
product__user = self.user
).annotate(
total_clients = Count( 'user', distinct = True )
)
return clients.total_clients
.and here's what it returns:
Exception Type: MultipleObjectsReturned
Exception Value: get() returned more than one Order -- it returned 2!
I could probably be running a query instead of using the orm but I don't want that. This is a rather simple query that I'm sure Django is handling very easily and I want to keep away from writing strings in my code.
Here's my model setup:
class UserProfile( models.Model ):
user = models.OneToOneField( User, related_name = 'profile' )
....
def get_total_clients( self ):
clients = Order.objects.get(
status = Order.COMPLETED,
product__user = self.user
).annotate(
total_clients = Count( 'user', distinct = True )
)
return clients.total_clients
class Order( models.Model ):
PENDING = 0
COMPLETED = 1
REFUNDED = 2
STATUS_CHOICES = (
(PENDING, "Pending"),
(COMPLETED, "Completed"),
(REFUNDED, "Refunded"),
)
user = models.ForeignKey( User, related_name = "orders" )
product = models.ForeignKey( Product, related_name = "orders" )
amount = models.DecimalField( max_digits = 6, decimal_places = 2, default = 0.00 )
status = models.SmallIntegerField(
choices = STATUS_CHOICES, default = PENDING, db_index = True
)
class Product( models.Model ):
user = models.ForeignKey( User, related_name = "unlocks" )
name = models.CharField( max_length = 255 )
Django queryset have a count method:
clients = Order.objects.filter(
status = Order.COMPLETED,
product__user = self.user
)
return clients.count()
If i got you right, you are intrested in the amount of consumers who ordered a product from a user. Some docs, that may be helpful.
My suggestion is:
result = Product.objects.distinct().filter(
# or any query:
orders__status=Order.COMPLETED,
user=default_user
).aggregate( # see the difference between aggregate() and annotate()
total_users=Count('orders__user', distinct=True)
)
I expect the result to be: {'total_users': NUM_OF_USERS}
In raw SQL it will be something like:
SELECT DISTINCT COUNT(DISTINCT "main_order"."user_id") AS
"total_users" FROM "main_product" INNER JOIN "main_order" ON (
"main_product"."id" = "main_order"."product_id" ) WHERE
("main_order"."status" = STATUS AND "main_product"."user_id" = USER_ID)
Is that what you wanted?

Using transaction to ensure 2 models save or neither save

I need to ensure that both Model1 and Model2 save or neither of them save and I'm pretty sure I should be able to do this in a transaction, however it doesn't seem to be working.
I'm not sure if I am understanding how transactions work properly. I thought that the transaction would be good for the entirety of the function call that was wrapped in the decorator.
I'm also not sure if this is just an Sqlite problem too.
models.py
from django.db import models
class Model1( models.Model ):
name = models.CharField( max_length = 20 )
number = models.IntegerField( )
class Model2( models.Model ):
name = models.CharField( max_length = 20 )
number = models.IntegerField( )
parent = models.ForeignKey( Model1 )
Sample Transaction Code
from django.db import transaction
#transaction.commit_manually
def somefunction( name ):
try:
a = Model1.objects.get( name = name )
except Model1.DoesNotExist:
a = Model1( name = name )
try:
b = Mode2.objects.get( name = name)
except Model2.DoesNotExist:
b = Model2( name = name )
a.number = 3
b.number = 'a'
try:
a.save()
b.parent = a
b.save()
transaction.commit()
except:
transaction.rollback()
I have not tried MySQL or Postgres as I have not actually found anything specifically saying Sqlite cannot do this, so just want to find out if I am doing something wrong or if it is indeed Sqlite3.
So the final answer was that Transactions were working fine it was just that my Unit Tests were not inheriting from
https://docs.djangoproject.com/en/1.2/topics/testing/#django.test.TransactionTestCase
Since I was inheriting from just TestCase, every test_ method was wrapped in a transaction and I could not control .commit() or .rollback() behaviour.
This was just an utter failure on my part to realize how test cases were run.