getting a django model field from another field - django-models

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)

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)

How to show the first x characthers of a Django model field in self representation

How can I use the first, let's say 10 characters of a field in the string representation of a Django model entry?
If I simply use {self.Post} I get the whole thing that might be too long. I tried to use {self.Post,10} but that doesn't really fly.
class Posts(models.Model):
Poster = models.ForeignKey(
User, on_delete=models.CASCADE, verbose_name="Poster")
PostCreated = models.DateTimeField(
auto_now_add=True, null=True, verbose_name="Post created")
Post = models.TextField(blank=True, verbose_name="Post")
PostEdited = models.BooleanField(
default=False, verbose_name="Has been edited")
PostHasComments = models.BooleanField(
default=False, verbose_name="Has comments")
def __str__(self):
return f"{self.Post} by {self.Poster}"
class Meta:
verbose_name_plural = "Posts"
You can slice the post, with:
def __str__(self):
return f'{self.Post[:10]} by {self.Poster}'

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

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.

How to solve some sort of chicken egg relation within ndb.Models?

I have two entities (events and users). Each user has several events, but I don't want them to be stored within a StructuredProperty, because in future it should be possible to have multiple creators/admins. Now I have the problem, that User needs the Event Class for definition and vice versa. How can I implement the intended structure?
Two models with mutual relations.
class Event(EndpointsModel):
_message_fields_schema = ("id", "name", "creator",
"datetime", "place", "category", "participants")
creator = ndb.KeyProperty(kind=User)
participants = ndb.KeyProperty(kind=User, repeated=True)
name = ndb.StringProperty()
datetime = ndb.DateTimeProperty(auto_now_add=True)
place = ndb.GeoPtProperty()
category = ndb.StringProperty(choices=('all', 'drinking'))
class User(EndpointsModel):
_message_fields_schema = ("id", "name", "password", "events")
name = ndb.StringProperty()
password = ndb.StringProperty()
events = ndb.KeyProperty(kind=Event, repeated=True)
def create_event(self, e_name, e_datetime, e_place, e_category):
event = Event(name=e_name, creator = self.key, datetime=e_datetime, place=e_place, category=e_category)
event.put()
self.events.append(event)
self.put()
def get_events(self):
return ndb.get_multi(self.events)
Error Message:
NameError: name 'User' is not defined
EDIT 1:
I changed the kind to a string, containing the class name, like Greg suggested it. But it does not work too.
class Category(EndpointsModel):
_message_fields_schema = ("id", "name", "parent")
name = ndb.StringProperty()
parent = ndb.KeyProperty(kind='Category', default=None)
class Event(EndpointsModel):
_message_fields_schema = ("id", "name", "creator", "datetime",
"place", "category")
participants = ndb.KeyProperty(kind='User', repeated=True)
creator = ndb.KeyProperty(kind='User')
name = ndb.StringProperty()
datetime = ndb.DateTimeProperty(auto_now_add=True)
place = ndb.GeoPtProperty()
category = ndb.KeyProperty(Category)
class User(EndpointsModel):
_message_fields_schema = ("id", "name", "password")
name = ndb.StringProperty()
password = ndb.StringProperty()
events = ndb.KeyProperty(Event, repeated=True)
Now I receive the following stack trace:
ERROR 2014-01-21 09:38:39,764 service.py:191] Encountered unexpected error from ProtoRPC method implementation: BadValueError (Expected Key, got [])
Traceback (most recent call last):
File "/home/chris/Downloads/google_appengine/lib/protorpc-1.0/protorpc/wsgi/service.py", line 181, in protorpc_service_app
response = method(instance, request)
File "/home/chris/Downloads/google_appengine/lib/endpoints-1.0/endpoints/api_config.py", line 1321, in invoke_remote
return remote_method(service_instance, request)
[...]
value = self._call_shallow_validation(value)
File "/home/chris/Downloads/google_appengine/google/appengine/ext/ndb/model.py", line 1227, in _call_shallow_validation
return call(value)
File "/home/chris/Downloads/google_appengine/google/appengine/ext/ndb/model.py", line 1274, in call
newvalue = method(self, value)
File "/home/chris/Downloads/google_appengine/google/appengine/ext/ndb/model.py", line 1927, in _validate
raise datastore_errors.BadValueError('Expected Key, got %r' % (value,))
BadValueError: Expected Key, got []
You can use strings in the KeyProperty constructor to refer to kinds that don't have a model definition:
class Event(ndb.Model):
participants = ndb.KeyProperty(kind='User', repeated=True)
You can not create such references to the entities. Here is somo solutions:
1. You must use normal StringProperty for Event.creator or other id for User instance
2. Remove evens from class User - you can reach to evens by index on class Events
3. Use third entity model like this:
class EventCreator(EndpointsModel):
creator = ndb.KeyProperty(kind=User)
event = ndb.KeyProperty(kind=Event)
and from class User remove creator & from class Event remove
You could specify the key properties without the kind parameter (it is optional) and then do a manual check in your constructor or a pre-put hook or something like that -- or maybe not even worry about the kind:
class Event(EndpointsModel):
creator = ndb.KeyProperty()
# Constructor option
def __init__(self, *args, **kwargs):
super(Event, self).__init__(*args, **kwargs)
if 'creator' in kwargs and kwargs['creator'] != 'User':
raise Exception('oh no')
# Hook option
_pre_put_hook(self):
if self.creator and self.creator.kind() != 'User':
raise Exception("oh no")
The actual syntax will probably be slightly different. Feel free to edit.

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