how to store only one value on my django models? - python

below is my model
from django.db import models
LEVEL_CHOICES = (('beg','beginner'),('ind','inter'),('exp','expert'))
class scrap(models.Model):
subject = models.CharField(max_length=128,unique=True)
# level= models.CharField(max_length=128,unique=True)
level = models.CharField(max_length=128, choices=LEVEL_CHOICES)
time = models.IntegerField(unique=True)
def __str__(self):
return self.subject
is there anyway so that i can only store one value per class.what i am doing is supplying values to this model's objects through a form.so i want only once instance of this class.if another value is supplied through through the form,what i want is the old value to be replaced and new value to be stored.

I'm not sure why do you really just keep one object for your model, but it's pretty easy to achieve:
if scrap.objects.exists():
current_scrap = Scrap.objects.all()[0]
else:
current_scrap = Scrap()
current_scrap.subject = new_subject
current_scrap.level = new_level
current_scrap.time = new_time
current_scrap.save()

Related

How to compare what values two models/querysets share in a view for later display in the template?

I'm trying to compare all the things in ModelOne with ModelTwo, to check which things are or are not in one or the other model, then put this in the view context for display in the template.
class Things(model.Model):
name = models.CharField()
class ModelOne(models.Model):
things = models.ManyToManyField(Things)
class ModelTwo(models.Model):
things = models.ManyToManyField(Things)
How would you do this?
one_instance = ModelOne.objects.get(id=one_id)
two_instance = ModelTwo.objects.get(id=two_id)
one_thing_ids = set(one_instance.things.values_list("id", flat=True))
two_thing_ids = set(two_instance.things.values_list("id", flat=True))
shared_thing_ids = one_thing_ids & two_thing_ids
thing_ids_in_one_not_in_two = one_thing_ids - two_thing_ids
thing_ids_in_two_not_in_one = two_thing_ids - one_thing_ids
shared_things = Thing.objects.filter(id__in=shared_thing_ids)
You can then pass shared_things queryset into the template for display.
If your Thing model only has a name field and the names are unique we can simplify a little by altering the model:
class Things(model.Model):
name = models.CharField(unique=True)
or even:
class Things(model.Model):
name = models.CharField(primary_key=True, unique=True)
(in this case the db table will not have an id column, it's not needed)
Either way we can then eliminate the extra Thing query at the end:
one_instance = ModelOne.objects.get(id=one_id)
two_instance = ModelTwo.objects.get(id=two_id)
one_thing_names = set(one_instance.things.values_list("name", flat=True))
two_thing_names = set(two_instance.things.values_list("name", flat=True))
shared_thing_names = one_thing_names & two_thing_names
thing_names_in_one_not_in_two = one_thing_names - two_thing_names
thing_names_in_two_not_in_one = two_thing_names - one_thing_names
...and just pass sets of string names into the template.

Storing and retrieving default values for fields in a related model instance

I would like to store default values for a model instance in a related object; for example, given this code:
class Contract(models.Model):
user = models.ForeignKey(User)
product = models.ForeignKey(Product)
duration = models.IntegerField(null=True, help_text='Contract validity (days)')
template = models.ForeignKey(ContractTemplate)
class ContractTemplate(models.Model):
name = models.CharField(max_length=100)
duration = models.IntegerField(help_text='Contract validity (days)')
I would like to store objects representing different common durations like:
yearly_contract = ContractTemplate.object.create(name='yearly', duration=365)
monthly_contract = ContractTemplate.object.create(name='monthly', duration=30)
and return the default value from the linked template when the object contract does not specify the value:
contract1 = Contract.objects.create(user=foo_user, foo_product, template=monthly_contract)
# contract1.duration should return 365
contract2 = Contract.objects.create(user=foo_user, foo_product, duration=45, template=monthly_contract)
# contract2.duration should return 45
So, what is the best way to achieve something like this?
You can use a callable object as default. Which seems to be what you want:
Have a look here:
https://docs.djangoproject.com/en/3.1/ref/models/fields/#default

How to update queryset value in django?

I have written a python script in my project. I want to update the value of a field.
Here are my modes
class News_Channel(models.Model):
name = models.TextField(blank=False)
info = models.TextField(blank=False)
image = models.FileField()
website = models.TextField()
total_star = models.PositiveIntegerField(default=0)
total_user = models.IntegerField()
class Meta:
ordering = ["-id"]
def __str__(self):
return self.name
class Count(models.Model):
userId = models.ForeignKey(User, on_delete=models.CASCADE)
channelId = models.ForeignKey(News_Channel, on_delete=models.CASCADE)
rate = models.PositiveIntegerField(default=0)
def __str__(self):
return self.channelId.name
class Meta:
ordering = ["-id"]
This is my python script:
from feed.models import Count, News_Channel
def run():
for i in range(1, 11):
news_channel = Count.objects.filter(channelId=i)
total_rate = 0
for rate in news_channel:
total_rate += rate.rate
print(total_rate)
object = News_Channel.objects.filter(id=i)
print(total_rate)
print("before",object[0].total_star,total_rate)
object[0].total_star = total_rate
print("after", object[0].total_star)
object.update()
After counting the total_rate from the Count table I want to update the total star value in News_Channel table. I am failing to do so and get the data before the update and after the update as zero. Although total_rate has value.
The problem
The reason why this fails is because here object is a QuerySet of News_Channels, yeah that QuerySet might contain exactly one News_Channel, but that is irrelevant.
If you then use object[0] you make a query to the database to fetch the first element and deserialize it into a News_Channel object. Then you set the total_star of that object, but you never save that object. You only call .update() on the entire queryset, resulting in another independent query.
You can fix this with:
objects = News_Channel.objects.filter(id=i)
object = objects[0]
object.total_star = total_rate
object.save()
Or given you do not need any validation, you can boost performance with:
News_Channel.objects.filter(id=i).update(total_star=total_rate)
Updating all News_Channels
If you want to update all News_Channels, you actually better use a Subquery here:
from django.db.models import OuterRef, Sum, Subquery
subq = Subquery(
Count.objects.filter(
channelId=OuterRef('id')
).annotate(
total_rate=Sum('rate')
).order_by('channelId').values('total_rate')[:1]
)
News_Channel.objects.update(total_star=subq)
The reason is that object in your case is a queryset, and after you attempt to update object[0], you don't store the results in the db, and don't refresh the queryset. To get it to work you should pass the field you want to update into the update method.
So, try this:
def run():
for i in range(1, 11):
news_channel = Count.objects.filter(channelId=i)
total_rate = 0
for rate in news_channel:
total_rate += rate.rate
print(total_rate)
object = News_Channel.objects.filter(id=i)
print(total_rate)
print("before",object[0].total_star,total_rate)
object.update(total_star=total_rate)
print("after", object[0].total_star)
News_Channel.total_star can be calculated by using aggregation
news_channel_obj.count_set.aggregate(total_star=Sum('rate'))['total_star']
You can then either use this in your script:
object.total_star = object.count_set.aggregate(total_star=Sum('rate'))['total_star']
Or if you do not need to cache this value because performance is not an issue, you can remove the total_star field and add it as a property on the News_Channel model
#property
def total_star(self):
return self.count_set.aggregate(total_star=Sum('rate'))['total_star']

How to automatically change model fields in django

I have a model and I want to know if it is possible to set a condition that triggers a change in the model field. For example, I have a model
class BillboardTracker(models.Model):
client_name = models.CharField(max_length=400)
entry_date = models.DateTimeField(default=datetime.now)
duration = models.PositiveIntegerField()
expiry_date = models.DateField()
is_expired = models.BooleanField(default=False)
I want to know if it is possible to have a function in the model that makes is_expired equals to True when the expiry date is up. I tried this
def expire(self):
if datetime.now == self.expiry_date:
self.is_expired = True
but it's not working. Is it possible to implement this?
Use a #property
The simplest thing here is not to have an is expired field at all! It's not needed. What you need is a property.
class BillboardTracker(models.Model):
client_name = models.CharField(max_length=400)
entry_date = models.DateTimeField(default=datetime.now)
duration = models.PositiveIntegerField()
expiry_date = models.DateField()
#property
def is_expired(self):
if datetime.now > self.expiry_date:
return True
return False
Remember, you don't have a field in a database, if that field is the same as another field with a simple calculation. This automatically eliminates your head ache of having to flag items as expired.
If you want to find out if an object has expired.
if instance.is_expired == True:
print 'yes, that ones gone'
Filtering
If you wanted to retrieve a whole set of objects that have expired
BillboardTracker.objects.filter(expiry_date__le=datetime.now())
This is why I mentioned that you don't need to store a field that can be easily calculated.
Index advantage
In most RDBMS a boolean field (such as your is_expired column) cannot be indexed effectively. So that actually means the above query will be faster than a query on that boolean field provided you create an index on the expiry_date field.
You need to make two changes in this function,
Firstly use datetime.now() and secondly,
You might want to update your logic like this :
def expire(self):
if datetime.now() >= self.expiry_date:
self.is_expired = True
return True
else:
return False
Because sometimes both the values might not be exactly same but still BillboardTracker need is_expired = True for all previous dates.
And in your views :
def your_view(request):
instance = BillboardTracker.objects.get(id=some_id)
if instance.is_expired() == True:
print 'expired'
else:
print 'fresh'

How do I store a string in ArrayField? (Django and PostgreSQL)

I am unable to store a string in ArrayField. There are no exceptions thrown when I try to save something in it, but the array remains empty.
Here is some code from models.py :
# models.py
from django.db import models
import uuid
from django.contrib.auth.models import User
from django.contrib.postgres.fields import JSONField, ArrayField
# Create your models here.
class UserDetail(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
key = models.CharField(max_length=50, default=False, primary_key=True)
api_secret = models.CharField(max_length=50)
user_categories = ArrayField(models.CharField(max_length = 1000), default = list)
def __str__(self):
return self.key
class PreParentProduct(models.Model):
product_user = models.ForeignKey(UserDetail, default=False, on_delete=models.CASCADE)
product_url = models.URLField(max_length = 1000)
pre_product_title = models.CharField(max_length=600)
pre_product_description = models.CharField(max_length=2000)
pre_product_variants_data = JSONField(blank=True, null=True)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
def __str__(self):
return self.pre_product_title
I try to save it this way:
catlist = ast.literal_eval(res.text)
for jsonitem in catlist:
key = jsonitem.get('name')
id = jsonitem.get("id")
dictionary = {}
dictionary['name'] = key
dictionary['id'] = id
tba = json.dumps(dictionary)
print("It works till here.")
print(type(tba))
usersearch[0].user_categories.append(tba)
print(usersearch[0].user_categories)
usersearch[0].save()
print(usersearch[0].user_categories)
The output I get is:
It works till here.
<class 'str'>
[]
It works till here.
<class 'str'>
[]
[]
Is this the correct way to store a string inside ArrayField?
I cannot store JSONField inside an ArrayField, so I had to convert it to a string.
How do I fix this?
Solution to the append problem.
You haven't demonstrated how your usersearch[0] I suspect it's something like this:
usersearch = UserDetail.objects.all()
If that is so you are making changes to a resultset, those things are immutable. Try this you will see that the id is unchanged too:
usersearch[0].id = 1000
print usersearch.id
But this works
usersearch = list(UserDetail.objects.all())
and so does
u = usersearch[0]
Solution to the real problem
user_categories = ArrayField(models.CharField(max_length = 1000), default = list)
This is wrong. ArrayFields shouldn't be used in this manner. You will soon find that you need to search through them and
Arrays are not sets; searching for specific array elements can be a
sign of database misdesign. Consider using a separate table with a row
for each item that would be an array element. This will be easier to
search, and is likely to scale better for a large number of elements
ref: https://www.postgresql.org/docs/9.5/static/arrays.html
You need to normalize your data. You need to have a category model and your UserDetail should be related to it through a foreign key.

Categories

Resources