django how to save a big model with for loop in views.py? - django-models

I have a very big model in models.py:
simplified version is:
class MyModel(models.Model):
item_1 = models.FloatField(null=True, blank=True)
...
item_20 = models.FloatField(null=True, blank=True)
in views.py:
def form_valid(self, form_class):
instance = form_class.save(commit=False)
for i in range(1, 20):
name = 'item_' + str(i)
instance.name = i
With this the field name 'item_1' ... to 'item_20' in instance is not recogniced. Instead 'name' is added to instance like other new field...
How can I iterate and save my model?
Any suggestion?
Thanks!!!

You should probably use setattr in order to loop through the fields and set the values in them. Try this:
def form_valid(self, form_class):
instance = form_class.save(commit=False)
for i in range(1, 20):
name = 'item_' + str(i)
setattr(instance, name, value) # Where value is the data you wanted to save in the field `name`
Similary user getattr() to get the data by looping through the class instance.

Related

How can update field by overriding save method which is in another app models

I have two models Bill and Payment each with 3 fields. Here I want to update field last_price directly when user pay bill. If user pay complete amount then it would be 0. or if user not pay complete amount then remaining amount want to be save in last_price. So here I want to update amount of last_bill directly when user pay bill.
Note: Both models are in separate app
My Fields are:
BillApp/models
Bill(model.Model):
bill_no = models.IntegerField(max_length = 100,primary_key=True)
last_price = models.IntegerField()
Name = models.CharField(max_length = 20)
PaymentApp/models
Payment(model.Model):
id = models.CharField(max_length = 100,primary_key=True)
bill_no = models.ForeignKey(Bill, on_delete = SET_NULL,null=True)
total_amount = models.CharField(max_length = 10)
def save(...):
Update value of Bill.last_price
How do I update value of Bill.last_price in the save method
I tried this for update field last_price
def save(self,*args, **kwargs):
new_last_price = self.total_amount - self.bill_no.last_price
print("new_last_price : ",new_last_price)
bill_detail = Bill.objects.filter(bill_no=self.bill_no).first()
print("bill_detail : ",bill_detail)
try:
with transaction.atomic():
updated_field = bill_detail.save(update_fields = ['last_price'])
print("updated_field : ", updated_field)
super().save(*args, **kwargs)
print(Bill.objects.filter(bill_no=self.bill_no).first().last_price)
except IntegrityError:
print('Exception in save')
I getting correct output of new_last_price and bill_detail..
but updated_field display None ..
How Can I save new value in Bill?
Your save method will save the data and refresh the object instance but will not return the object instance. Use directly show last price.
bill_detail.save(update_fields = ['last_price'])
print(bill_detail.last_price)

getting a django model field from another field

i have this model
class Person(models.Model):
picture = models.ImageField(
default='default.jpg', upload_to='profile_pics', )
firstName = models.CharField(blank=True, max_length=100)
familyName = models.CharField(blank=True, max_length=100)
age = models.IntegerField(default=0)
GENDER = [
("M", 'Male'),
("F", 'Female'),
("U", 'UNKNOWN'),
]
gender = models.CharField(
max_length=2,
choices=GENDER,
default="U",
)
address = models.CharField(blank=True, max_length=100)
remark = models.TextField(default="no remark")
description_vector = models.TextField(blank=True)
i want to infer the description_vector from the picture field (with a method called identifie(pictur) that return a string ) whenever i add a new Person or changing a model image (if the image didn't change i dont want to change the description_vector)
i know i can use the save method like here but i dont know how to specify that when the image change the vector change.
i dont know if it changes anything but i use django-rest-framowrk to add and change persons
i know
I am not sure I understand what your specific doubt is, but I think this might be helpful.
def save(self, *args, **kwargs):
if self.id is not None: # check only when update
original_picture = self.objects.get(id=self.id).picture
if original_picture !== self.picture # You must add here your method to evaluate if both images are equal
self.vector = some_method_to_change_vector(self.picture)
return super().save(*args, **kwargs)

How to create an object attribute without writing to database -- peewee -- python

Maybe i have an understanding problem. I try to make 2 tabeles in one database. But additionaly i need to have some temporary values in one class that i doen´t want to write to the database.
I try to switch to peewee and read the dokumentation but i find no solution at my own.
without peewee i would make an init method where i write my attributes. But where did i have to write them now?
from peewee import *
import datetime
db = SqliteDatabase('test.db', pragmas={'foreign_keys': 1})
class BaseModel(Model):
class Meta:
database = db
class Sensor(BaseModel):
id = IntegerField(primary_key=True)
sort = IntegerField()
name = TextField()
#def __init__(self):
#self.sometemporaryvariable = "blabla"
def meineparameter(self, hui):
self.hui = hui
print(self.hui)
class Sensor_measure(BaseModel):
id = ForeignKeyField(Sensor, backref="sensorvalues")
timestamp = DateTimeField(default=datetime.datetime.now)
value = FloatField()
class Meta:
primary_key = CompositeKey("id", "timestamp")
db.connect()
db.create_tables([Sensor_measure, Sensor])
sensor1 = Sensor.create(id=2, sort=20, name="Sensor2")
#sensor1.sometemporaryvariable = "not so important to write to the database"
sensor1.save()
Remember to call super() whenever overriding a method in a subclass:
class Sensor(BaseModel):
id = IntegerField(primary_key=True)
sort = IntegerField()
name = TextField()
def __init__(self, **kwargs):
self.sometemporaryvariable = "blabla"
super().__init__(**kwargs)

DRF - queryset filter using contains field lookup on SlugRelatedField

I am struggling to figure out the how to run queryset filter using "field__contains" on a SlugRelatedField.
I have a simple Book model and a Tag model that looks as following:
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
class MetaTag(models.Model):
book = models.ManyToManyField('Book', related_name='meta_tags',
help_text='The book this meta tag belongs to')
value = models.CharField(max_length=400, unique=True, help_text='Meta tag value')
class BookSerializer(serializers.HyperlinkedModelSerializer):
class BookHyperlink(serializers.HyperlinkedIdentityField):
"""A Hyperlink field for book details"""
def get_url(self, obj, view_name, request, format):
url_kwargs = {
'pk': obj.id,
}
return reverse(view_name, kwargs=url_kwargs, request=request, format=format)
url = BookHyperlink(view_name='book-detail')
meta_tags = CreatableSlugRelatedField(many=True, slug_field='value', queryset=MetaTag.objects.all())
class Meta:
model = Book
fields = (
'id',
'title',
'publisher',
'publication_date',
'meta_tags',
'url'
)
class MetaTagSerializer(serializers.ModelSerializer):
class Meta:
model = MetaTag
fields = ('id', 'book', 'value',)
class CreatableSlugRelatedField(serializers.SlugRelatedField):
def to_internal_value(self, data):
try:
return self.get_queryset().get_or_create(**{self.slug_field: data})[0]
except ObjectDoesNotExist:
self.fail('does_not_exist', slug_name=self.slug_field, value=smart_text(data))
except (TypeError, ValueError):
self.fail('invalid')
class Meta:
model = MetaTag
fields = ('id', 'book', 'value', )
Now in my BooksView, I want to be able to filter the queryset by meta_tags value. I've tried the following with "__contains" field lookup:
class Books(viewsets.ModelViewSet):
"""Default view for Book."""
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = (IsAuthenticated, )
filter_backends = (DjangoFilterBackend,)
filter_fields = tuple(f.name for f in Book._meta.get_fields())
def get_queryset(self):
search_pattern = self.request.query_params.get('search', None)
if search_pattern is not None and search_pattern is not '':
self.queryset = self.queryset.filter(meta_tags__contains = search_pattern)
return self.queryset
def get_object(self):
if self.kwargs.get('pk'):
return Book.objects.get(pk=self.kwargs.get('pk'))
But I get the following error from django:
File "~MyProject/venv/lib/python3.6/site-packages/django/db/models/sql/query.py", line 1076, in build_lookup
raise FieldError('Related Field got invalid lookup: {}'.format(lookup_name))
django.core.exceptions.FieldError: Related Field got invalid lookup: contains
Which as I understand means that since "meta_tags" is not a regular array or Text field, the contains field lookup cannot be applied on that field.
What is the best way if so to filter the queryset in such case for meta_tags value?
A django expert I've consulted about this issue, suggested to try append the "slug_field" ("__value" in this case) to "__contains" field lookup when used with external model.
It was not documented anywhere or even on django official documentation at https://docs.djangoproject.com/en/2.0/ref/contrib/postgres/fields/#contains, so I had no way to know it works this way, but this solution actually works:
queryset = queryset.filter(meta_tags__value__contains=search_pattern)
It actually makes sense when you look deeper at the MetaTag model, as "value" is the inner field of the meta_tags model:
class MetaTag(models.Model):
book = models.ManyToManyField('Book', related_name='meta_tags',
help_text='The book this meta tag belongs to')
value = models.CharField(max_length=400, unique=True, help_text='Meta tag value')
def __str__(self):
return '%s > %s' % (self.channel, self.value)
The reason it was not so obvious to append __value at the first place is because meta_tags array (array of objects) is flattened using the SlugRelatedField serializer where only the slug_field is projected and the rest fields are omitted.
So the final output of meta_tags array is flat:
meta_tags: ['tag1','tag2']
instead of:
meta_tags: [{book: 'a', value: 'tag1'},{book: 'a', value: 'tag2'}]
But since serialization on django DRF is made on a late stage (after queryset is completed) the original field schema should be considered.
Hope this will save somebody's headache someday.

effective counting of objects

I have 2 models:
Category(models.Model):
name = models.CharField(max_length=30)
no_of_posts = models.IntegerField(default=0) # a denormalised field to store post count
Post(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=100)
desc = models.TextField()
user = models.ForeignKey(User)
pub_date = models.DateTimeField(null=True, blank=True)
first_save = models.BooleanField()
Since I always want to show the no. of posts alongwith each category, I always count & store them every time a user creates or deletes a post this way:
## inside Post model ##
def save(self):
if not pub_date and first_save:
pub_date = datetime.datetime.now()
# counting & saving category posts when a post is 1st published
category = self.category
super(Post, self).save()
category.no_of_posts = Post.objects.filter(category=category).count()
category.save()
def delete(self):
category = self.category
super(Post, self).delete()
category.no_of_posts = Post.objects.filter(category=category).count()
category.save()
........
My question is whether, instead of counting every object, can we not use something like:
category.no_of_posts += 1 // in save() # and
category.no_of_posts -= 1 // in delete()
Or is there a better solution!
Oh, I missed that! I updated the post model to include the relationship!
Yes, a much better solution:
from django.db.models import Count
class CategoryManager(models.Manager):
def get_query_set(self, *args, **kwargs):
qs = super(CategoryManager, self).get_query_set(*args, **kwargs)
return qs.annotate(no_of_posts=Count('post'))
class Category(models.Model):
...
objects = CategoryManager()
Since you didn't show the relationship between Post and Category, I guessed on the Count('posts') part. You might have to fiddle with that.
Oh, and you'll want to get rid of the no_of_posts field from the model. It's not necessary with this. Or, you can just change the name of the annotation.
You'll still be able to get the post count with category.no_of_posts but you're making the database do the legwork for you.

Resources