Incrementing IntegerField counter in a database - python

As beginner at Django, i tried to make a simple application that would give Http response of how many times content was viewed.
I have created a new Counter model, and inside, added IntegerField model count.
class Counter(models.Model):
count = models.IntegerField(default=0)
def __int__(self):
return count
In views, i made a variable counter out of Counter() class, and tried adding +1 to counter.count integer, but when i tried to save, it would give me an error that integer couldn't be saved.
so i tried saving class instead:
def IndexView(response):
counter = Counter()
counter.count = counter.count + 1
counter.save()
return HttpResponse(counter.count)
This method, would keep showing 1 and could not change after reload.
How would i change IntegerField model properly, so it could be updated after every view, and would be saved even if server was reloaded?

The problem
Yes but you are creating a new Counter object on each request, which starts again at 0, that's your problem
def IndexView(response):
counter = Counter() # This creates a new counter each time
counter.count = counter.count + 1
counter.save()
return HttpResponse(counter.count)
What you were doing above would result in a bunch of Counter objects with count = 1 in the database.
The Solution
My example below shows you how to get an existing Counter object, and increment it, or create it if it doesn't already exist, with get_or_create()
First we need to associate a Counter to e.g. a page (or anything, but we need someway to identify it and grab it from the DB)
class Counter(models.Model):
count = models.IntegerField(default=0)
page = models.IntegerField() # or any other way to identify
# what this counter belongs to
then:
def IndexView(response):
# Get an existing page counter, or create one if not found (first page hit)
# Example below is for page 1
counter, created = Counter.objects.get_or_create(page=1)
counter.count = counter.count + 1
counter.save()
return HttpResponse(counter.count)
Avoid race conditions that can happen with count = count + 1
And to avoid race conditions use an F expression
# When you have many requests coming in,
# this may have outdated value of counter.count:
# counter.count = counter.count + 1
# Using an F expression makes the +1 happen on the database
from django.db.models import F
counter.count = F('count') + 1

Related

Expected singleton: sezonekip() error pycharm

This is my code and i get a singleton error. I am trying to build a football database and in this function I try to update teams position based on the result of a match.
#api.multi
#api.depends('gola_home', 'gola_away')
def perditeso_piket(self):
for record in self:
if record.gola_home > record.gola_away:
self.sezonekip_home_id.fitore += 1
self.sezonekip_away_id.humbje += 1
elif record.gola_home == record.gola_away:
self.sezonekip_home_id.barazime += 1
self.sezonekip_away_id.barazime+=1
else:
self.sezonekip_home_id.humbje += 1
self.sezonekip_away_id.fitore+=1
You're using/programming it halfway correct. Looping on self is the safe way because it could be a multi record recordset. But you missed to use record later on in your incrementations.
Those incrementations are nothing more then setting a new value to the fields. But if self is a multi record recordset (not a single record recordset, which is called singleton) Odoo isn't allowing a direct write/update. So just replace self with record in your incrementations:
#api.depends('gola_home', 'gola_away')
def perditeso_piket(self):
for record in self:
if record.gola_home > record.gola_away:
record.sezonekip_home_id.fitore += 1
record.sezonekip_away_id.humbje += 1
elif record.gola_home == record.gola_away:
record.sezonekip_home_id.barazime += 1
record.sezonekip_away_id.barazime+=1
else:
record.sezonekip_home_id.humbje += 1
record.sezonekip_away_id.fitore+=1
A mistake that often happens.
When you want to change a record it cycles to self, like you did, but all other fields must also be on record
example
def method(self):
for record in self:
record.first_name = "Mark"
record.last_name = "White"
This allows editing of that specific record. For example, the current registry

defining a variable in a class [duplicate]

This question already has answers here:
How can I access "static" class variables within methods?
(6 answers)
Closed 2 years ago.
I am trying to place a method in a class that will replace null values with running average of non null values. Please refer to the code below:
class ElNinoData(object):
# Your implementation here
add = 0
counter = 0
average = 0
# methods
def __init__(self , object):
self.object = object
def get_humidity(self):
if not math.isnan(self.object['humidity']):
global add
global counter
global average
add += self.object['humidity']
counter += 1
average = add/counter
else:
self.object['humidity'] = average
return self.object['humidity']
While executing this class in a method, I am getting the following error:
<ipython-input-40-c52c3ac6484b> in get_humidity(self)
19 global average
20
---> 21 add += self.object['humidity']
22 counter += 1
23 average = add/counter
NameError: name 'add' is not defined
Could anyone please explain the error,I am relatively new to python?
class ElNinoData(object):
add = 0
The add here is not a global variable,
global add
therefore this does not give access to it, but instead looks for that actual global.
add += self.object['humidity']
+= requires the variable to exist already (so that the old value can be added to), and you don't have a global add, nor a local one - you have a class attribute.
To access it directly, you can use ElNinoData.add, just as you would from code outside the class. Similarly for the other class attributes.
Looking up the class attribute via the instance - self.add - also succeeds. However, modifying the value then attaches a separate attribute to the instance, and leaves the class attribute unaffected.
If you want the members to be specific to an object, change it to this
class ElNinoData(object):
# Nothing here
# methods
def __init__(self , object):
self.add = 0
self.counter = 0
self.average = 0
self.object = object
and continue to refer to them with self..
If you want them to be class-wide, leave them where they are, but refer to them via ElNinoData..
In any case, you shouldn't use global here.
I think the problem is that your values add, counter and average are not global.
In order to set these values on your class instance, you have to use self instead.
Something like this:
def get_humidity(self):
if not math.isnan(self.object['humidity']):
self.add += self.object['humidity']
self.counter += 1
self.average = add/counter
else:
self.object['humidity'] = average
return self.object['humidity']
What you are missing here is that you have initialized the variables but not given them any values and then you are adding the values using +=, due to which python is throwing you this error.
Refer to the below code:-
class ElNinoData(object):
# Your implementation here
add = 0
counter = 0
average = 0
# methods
def __init__(self , object):
self.object = object
def get_humidity(self):
if not math.isnan(self.object['humidity']):
global add
global counter
global average
add = 0
counter = 1
add += self.object['humidity']
counter += 1
average = add/counter
else:
self.object['humidity'] = average
return self.object['humidity']
t = {'humidity' : 10}
a =ElNinoData(t)
a.get_humidity()

Multhreading with updates to MySQL

I need to evaluate around 80k rows of data every day at 11am, and I hope to accomplish it within a few minutes.
I used multithreading that uses select_for_update() of Django that gets one row at a time, updates it, and then gets a new row.
The problem is, there is an issue where the counter increases too fast having the assumption that there are times where the row gets evaluated twice.
Here is my current code block:
while True:
with transaction.atomic():
user_history = UserHistory.objects.select_for_update().filter(is_finished=False).first()
if user_history:
user = UserProfile.objects.filter(id=user_history.user_profile_id).first()
card_today = CardToday.objects.filter(id=user_history.card_today_id).first()
rewarded_value = 0
if user_history is item1:
if card_today.item1 > card_today.item2:
rewarded_value = card_today.item2/card_today.item1 + 1
elif user_history is item2:
if card_today.item2 > card_today.item1:
rewarded_value = card_today.item1/card_today.item2 + 1
user.coins += float(user.coins) + rewarded_value # the value increases too high here
user.save()
user_history.save()
else
break
This is the model for Card Today:
class CardToday(models.Model):
item1 = models.IntegerField(default=0)
item2 = models.IntegerField(default=0)
This is the model for User History:
class UserHistory(models.Model):
card_today = models.ForeignKey(CardToday, on_delete=models.CASCADE)
answer_item = models.ForeignKey(Item, on_delete=models.CASCADE)
is_finished = models.BooleanField(default=False) // checks whether the card has already been evaluated.
rewarded value's computation is as follows:
rewarded_value = majority/minority + 1
majority and minority switches depending on which item has a greater value.
Each user_history can only choose between item1 or item2.
After a certain amount of time has passed, the code will evaluate which item has been picked on a CardToday.
Is there a better way of accomplishing this?
The framework I'm using is Django, and I have a cron job running from the library django-cron.

Appending objects to a global list in python

I am trying to get the user to make an object called a NoteSet, each NoteSet will be put into a global list called db. This is my attempt at making this:
import sys
import datetime
db = list()
class NoteSet:
nextseqNum = 0
def __init__(self,name,description,hidden):
global db
self.seqNum = Note.nextseqNum
self.name = name
self.description = description
self.dateCreated = datetime.date.today()
self.hidden = hidden
self.notes = list()
db[self.seqNum] = self
print(self)
print(len(db))
Note.nextseqNum += 1
When I try to create an object for example:
NoteSet('example','ex',True)
It gives me the error
Traceback (most recent call last):
File "", line 1, in
NoteSet('example','ex',True)
File "C:\Users\Brandon\Desktop\step5.py", line 22, in init
db[self.seqNum] = self
IndexError: list assignment index out of range
Is this the right way to make a global list of objects?
As #aruisdante said you will need to append to the list
Try this:
db = []
class ListObj:
def __init__(self, name, msg, hide=False):
self.name = name
self.msg = msg
self.hide = hide
db.append(self)
Good Luck!
You get this error because db has no elements in it (python lists are initialized to length 0), so when you try and replace the element at location self.seqNum, you are acessing an invalid index. It has nothing to do with the global-ness of it.
If we assume that this global list is only ever going to be accessed in a thread-safe manner, you should simply be able to do:
db.appened(self)
Instead. However, as mentioned in the comments, it makes more sense in this use case to make db a class variable if this class is the 'gate keeper' to interfacing with the db list.
UPDATE
To address the OP's question in the comments,
I am looking to be able to keep track of the location of the objects in the list by the seqNum
As currently written, seqNum will always increment linearly, forever, with each new NoteSet instance. If we assume thread-safe access of Note.nextseqNum, then what you're trying to do via db[self.seqNum] is already implicitly done via db.append(self), because len(db) == Note.nextseqNum, always. For now, we're going to ignore what happens if you cand remove elements from db, because right now your system doesn't account for that at all and would completely break anyway.
If, however, in the future seqNum doesn't just increase monotonically forever each time you make a new instance, you can simply make db a dict instead of a list:
db = dict()
And then insert the new instance to it exactly as you are currently,
db[self.seqNum] = self
db now represents a mapping of a seqNum to a NoteSet explicitly, rather than an implicit relationship based on an array index.
I would actually recommend doing it this way anyway, as it will also solve the problem of removing items from db for 'free'. As is, doing del db[instance.seqNum] will completely invalidate all mappings of seqNum into db for any instance that came after the removed instance. But if db is a dict, then this operation does what you expect it to and all of the seqNum values still map to the correct instance in db.
So, to bring it all together, I would recommend you alter your class to look like the following:
import sys
import datetime
class NoteSet:
nextseqNum = 0
db = dict()
def __init__(self,name,description,hidden):
self.seqNum = NoteSet.nextseqNum
self.name = name
self.description = description
self.dateCreated = datetime.date.today()
self.hidden = hidden
self.notes = list()
NoteSet.db[self.seqNum] = self
print(self)
print(len(db))
NoteSet.nextseqNum += 1

Class does not seem to be Global in python

I setup a class and it accepts and prints out the variables fine in one if statement.
class npc: #class for creating mooks
def __init__(self, name):
self.name = name
def npc_iq (self,iq):
self.iq = []
def npc_pp (self,pp):
self.pp = []
def npc_melee (self, melee):
self.melee = []
def npc_ct (self, ct):
self.ct = []
It works fine in this if statement
if menu_option == 1:
print "Choose melees for npc"
init_bonus = random.randint(0,2)
char_PP = random.randint(7,15)
char_iq = random.randint(7,15)
npc_Melees = int(raw_input(prompt))
combat_time = math.floor((round_attacks - init_bonus - math.floor(char_PP/2) - math.floor(char_iq/2)) / npc_Melees)
#function for calculating sequence number
print "combat time is"
print combat_time
mook = "mook%s" % counter # adds different mook names to program
mook = npc(mook)
mook.iq = (char_iq)
mook.pp = (char_PP)
mook.melee = (npc_Melees)
mook.ct = (combat_time)
counter += 1
But on this statement it will print out the name in the class but not ct.
elif menu_option ==4:
print "Printing out all mooks"
print
printcount = counter -1
while printcount != 0:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
print
printcount -= 1
Why would a mookprint have any idea what value ct should be? The constructor for npc initialises a new instance of npc, with the name given as a parameter, but ct is left empty.
When you create an NPC in menu option 1, you do not create a global instance of npc. If you want to refer to a previously created instance of npc, you will need to find some way of storing them. Dictionaries may be a good solution for you. A dictionary is an object that holds mappings between keys and values. If you know the key, then you can find the assosicated value. In this case you would make name the key and the value the npc instances.
eg.
npcsDict = dict()
if menu_option == 1:
# code for intialising a new instance of npc
...
# most, if not all of the initialisation code should be moved to the
# __init__ method for npc
# now store the newly created mook
npcsDict[mook.name] = mook
elif menu_option == 4:
print "Printing out all mooks"
print
for mookName in npcsDict:
print npcsDict[mookName].name
print npcsDict[mookName].ct
print
i dont really understand your problem.
your working example:
mook = npc(mook)
mook.iq = (char_iq)
mook.pp = (char_PP)
mook.melee = (npc_Melees)
mook.ct = (combat_time)
mook.ct is value of (combat_time)
your failing example:
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
mookprint.ct's value is nothing because it is never set.
The elif will only be executed if the if has not, so if the elif block runs, ct was never set
I don't think you're understanding how four lines work:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.ct
Every time this block of code is run, the following things are happending:
You're assigning a string of the form "mook" to the variable mookprint
You're creating a new instance of the npc class. You should note that all of the instances you're creating will be separate from eachother. This new instance will have an attribute with the name that was previously held in the variable mookprint and this instance of npc will be assigned to mookprint.
You're printing the name attribute of the instance of the npc class that you created in the previous step. This works because when this instance was created, the __init__ method of your class was called with the argument name being set to "mook1" or whatever was stored in mookprint at the time.
You're printing the ct attribute of the instance of the npc class that you just created. Since you never set the ct attribute to anything, this will not work how you expected.
If you want to count the number of instances of your npc class, you'll need to create a class attribute. This is a variable whose value is common across all instances of a class. To do so, you'll need to modify your class definition to add an item to this attribute every time you make a new instance of the class. It will look something like this:
class npc: #class for creating mooks
ct = []
def __init__(self, name):
self.name = name
self.ct.append(name)
def get_ct(self):
return len(self.ct)
With the above, the variable ct will be a list that is common to all instances of npc and will grow every time a new npc is created. Then the method get_ct will count how long this list is.
Then you'll need to modify the four lines I mentioned to look like:
mookprint = "mook%s" % printcount
mookprint = npc(mookprint)
print mookprint.name
print mookprint.get_ct()
I think the code above shows how to change your code to work more how you expected it to work. However, it should be noted that you rarely want to create classes where each instance depends on information about the other instances. It is usually a better design to do something like Dunes suggested, storing the instances in a dictionary, or some other data structure, and keeping track of them that way.

Categories

Resources