Django - how to implement lock data - python

I have a database table. Some database items can be edited by a user, but only one user can edit the table content at a time, and if after 2 hours the user hasn't finished editing, other users can edit the table. How can I do this?
The table is like this:
class NodeRevision(BaseModel, NodeContent):
node = models.ForeignKey(Node, related_name='revisions')
summary = models.CharField(max_length=300)
revision = models.PositiveIntegerField()
revised_at = models.DateTimeField(default=datetime.datetime.now)
suggested = models.BooleanField(default=False)
suggest_status = models.CharField(max_length=16,default="")
Should I add a BooleanField to it, such as editing_locked=models.BooleanField(default=False) ? Or something else? And how could I implement the 2 hour check?

You'd need a locked_at time field and locked_by field.
Every time somebody loads an edit page, update the database with the locked_at and locked_by information.
To implement the 2 hour restriction, I'd just have the results calculated only when a user asks for permission (as opposed to polling / updating models). When a user tries to edit a model, have it check locked_by/locked_at and return a Boolean whether it's editable by the user or not.
def can_edit(self, user):
if user == self.locked_by:
return True
elif self.locked_at and (self.locked_at - datetime.datetime.now()).total_seconds > 2*60*60:
return True
return False

Related

Looping over a specific field in a django form

Learning Django by creating an eBay like bidding application.
One of my models is a simple bid class that will record every user's bid for a particular listing.
models.py
class Bid(models.Model):
bid = models.DecimalField(max_digits=10, decimal_places=2)
user = models.ForeignKey(User, on_delete=models.CASCADE)
listing = models.ForeignKey(Listing, on_delete=models.CASCADE)
forms.py
def listing_view(request, id):
form = BidForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.listing_id = id
# check bid is higher than starting bid or other bids or flash error message
current_bid = instance.bid
starting_bid = instance.listing.starting_bid
query = Bid.objects.all().filter(listing=id)
In forms.py, I am writing a view to validate that the bid the user entered is larger than existing bids. It's incomplete because I am stuck on creating this comparison.
The easy way is to loop over the 'query' variable and do the comparison, but is there a more elegant solution?
I found a solution using the all() function, something like:
all(current_bid > i in my_list)
But this only works for comparing against a list, not form objects
Is there a way to loop over query (i.e. for each in query) and get check whether current_bid is greater than all of the 'each.bid' in 1 line?
Something like this:
all(current_bid > i for i.bid in query)
Sadly, this doesn't work. I get a NameError - name 'i' is not defined.
Thanks!
This line here:
all(current_bid > i for i.bid in query)
needs to be changed to something like this:
all(current_bid > i.bid for i in query)
Sometimes list comprehensions are confusing so I like to imagine them as normal for loops. Here's my interpretation of what you're trying to do (no guarantees of correctness):
is_biggest_bid = True
for i in query:. # writing i.bid wouldn't make sense here
if i.bid > current_bid:
is_biggest_bid = False
break
Hope that helps.

Accessing the time where a field has been updated (Django models)

I need to create a system where the user have to change his password every "x" number of days. I managed to achieve this by saving a UTC timestamp representing the time when the user has created/modified his password.
My question is, instead of saving the timestamp in the database, is it possible to get a timestamp representing the time when the field has been changed through some of django's built-in functions? This way I could access when was the last time the user has updated his password without having to add a dedicated field in the model to save the timestamp in it.
No, there is no field that shows you which field updated in which time, you should implement it your self
you should use set_password everywhere to manage update password time
password_updated_at = models.DateTimeField(_('password updated at'))
def set_password(self, raw_password):
r = super(User, self).set_password(raw_password=raw_password)
self.password_updated_at = timezone.now()
self.save()
return r
def is_password_updated_at_expired(self):
duration = (timezone.now() - self.password_updated_at).total_seconds()
return duration > settings.PASSWORD_UPDATE_EXPIRE_TIME, duration

Bulk update in django with calculations

I have 2 models in my project:
class Currency(models.Model):
title = models.CharField(max_length=100, unique=True)
value = models.FloatField()
class Good(models.Model):
name = models.CharField(max_length=100)
slug = SlugField(max_length=100, unique=True)
cost_to_display = models.IntegerField(default=0)
cost_in_currency = models.IntegerField()
currency = models.ForeignKey(Currency)
The idea of such model is to speed up the search by price and have all goods in one currency.
Therefore I need some hook which will update all Goods in case exchange rate was updated.
In raw sql it will looks like this
mysql> update core_good set cost_to_display = cost_in_currency * (select core_currency.value from core_currency where core_currency.id = currency_id ) ;
Query OK, 663 rows affected (0.10 sec)
Rows matched: 7847 Changed: 663 Warnings: 0
Works pretty fast. Though I tried to implement the same in django admin like this (using bulk-update):
def save_model(self, request, obj, form, change):
"""Update rate values"""
goods = Good.objects.all()
for good in goods:
good.cost_to_display = good.cost_in_currency * good.currency.value
bulk_update(goods)
obj.save()
It takes up to 20 minutes to update all records via django admin this way.
What I am doing wrong? What is the right way to update all the prices?
This is purely untested, but it's sort of work in my mind:
from django.db.models import F
Good.objects.all().update(cost_to_display=F('cost_in_currenty') * F('currency__value'))
Even you are calling bulk_update, you still looped through all goods, which is why your process is slow.
Edit:
This won't work because F() doesn't support joined fields. It can be done using raw query.
For the future readers: any call to good.currency in your code is hitting the database. Consider using select_related to fetch Currency and Good objects in one query:
goods = Good.objects.select_related('currency')
Also now Django comes with bulk_update method since version 2.2 docs

Django. Python. How to get current user's activity log?

I need to pass the current user's entire activity log to an html page, but it seems I cannot find any helpful solution regarding the same.
Is it possible? If yes, please direct me in the right way?
Thanks in advance!
Update:
I found a solution making use of a get() call to django's LogEntry model, but I am clueless as to what shall be the appropriate parameters for doing the same.
Yet another UPDATE:
I am looking for a way to access the activity log of a particular user from the django's log entries WITHOUT saving it to any database
Take a look below listed.....Hope it will help::
lets example::
Create Two Field in Models:
last_activity_ip = models.IPAddressField()
last_activity_date = models.DateTimeField(default = datetime(1960, 1, 1))
user = models.OneToOneField(User, primary_key=True)
Since the User and UserActivity models are now related one-to-one we can now type:
Run the Query Like this:
a = User.objects.get(username__exact='mpcabd')
print a.useractivity.last_activity_ip
b = UserActivity.objects.get(user=a)
print b.user.username
** To track the activity use this **
activity = None
try:
activity = request.user.useractivity
except:
activity = UserActivity()
activity.user = request.user
activity.last_activity_date = datetime.now()
activity.last_activity_ip = request.META['REMOTE_ADDR']
activity.save()
return
activity.last_activity_date = datetime.now()
activity.last_activity_ip = request.META['REMOTE_ADDR']
activity.save()
This question don't has a short answer, you can use sentry project by side of main django project. below link can helping you:
https://sentry.readthedocs.org/en/latest/

How to select and limit the related_name connection in the Peewee ORM?

I'm using Flask with the Peewee ORM in which I have defined two tables like so:
class Ticket(db.Model):
created = DateTimeField(default=datetime.now)
customer_uuid = CharField() # the customer's UUID gotten from App. More info comes from bunq API.
ticket_type = ForeignKeyField(TicketType, related_name='tickets')
active = BooleanField(default=True)
class Assign(db.Model):
created = DateTimeField(default=datetime.now)
ticket = ForeignKeyField(Ticket, related_name='assigned_to')
user = ForeignKeyField(User, related_name='assigned_tickets')
In the Assign table, several users can be assigned to a ticket, but only the last one counts (i.e., if a new user gets assigned, the previous ones should be disregarded). So I select the active tickets using the following:
open_tickets = Ticket.select().where(Ticket.active == True)
I now want to use this loop in my template. With every iteration however, I also want to display the assigned user. But open_ticket[0].assigned_to obviously returns several assignments, and with it several users.
Would anybody know how I can get the latest assigned user for every ticket within a loop?
This worked for me in Sqlite:
q = (Ticket
.select(Ticket, Assign, User)
.join(Assign)
.join(User)
.group_by(Ticket)
.order_by(Ticket.id, Assign.id.desc()))
for ticket in q:
print ticket.id, ticket.assign.user.username

Categories