Django getting the revisions - django

I am working on a Django project where in a model file I have a field called 'status' which is a choice field.
STATUS_CHOICES = (
(StudentProfileStatus.NEW, _('New')),
(StudentProfileStatus.IMPORTED, _('Imported')),
(StudentProfileStatus.MISSING_INFORMATION, _('Missing Information')),
(StudentProfileStatus.INTERVIEW_SCHEDULED, _('Interview scheduled')),
(StudentProfileStatus.REJECTED, _('Rejected')),
(StudentProfileStatus.ACCEPTED, _('Accepted into Pool')),
(StudentProfileStatus.PAUSED, _('Paused')),
(StudentProfileStatus.OUTBOARDED, _('Outboarded'))
status = models.CharField(
_('Status'),
max_length=50,
choices=STATUS_CHOICES,
default=StudentProfileStatus.default()
)
I am sending an auto generated email as soon as 'Accepted into pool' choice option is selected by the admin for the first time.I am using post_save signal for that.Now in case an admin selects another choice option for e.g. 'Paused',saves it but later again reverts back to the 'Accepted into the pool' choice I don't want the mail to be sent out again.Is there any way where I can get all previous saved values for the 'status' field and check if 'Accepted into Pool' was already saved before so that there is no need to send out the mail again? One possible option which I could think of is django-revisions.

Related

Uploaded and Deadline fields in Django models

I am making a to do app and in models I have a Task model and dateTimeFields "uploaded", which has auto_now_add=True. Now I want to add deadline field, where users can add the task deadlines. I tried adding second DateTimeField but there's a problem when I make migrations. here's the code and the powershell warning:
models:
class Task(models.Model):
title = models.CharField(max_length=50)
completed = models.BooleanField(default=False)
uploaded = models.DateTimeField(auto_now_add=True)
deadline = models.DateTimeField()
def __str__(self):
return self.title
powershell:
*You are trying to add a non-nullable field 'deadline' to task without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
Provide a one-off default now (will be set on all existing rows with a null value for this column)
Quit, and let me add a default in models.py
Select an option:*
But there's a problem when I make migrations.
The problem is that the database might already have records, so what will you do to the Tasks in the database that now need a value for the existing records.
You can for example decide to work with the current timestamp. You can do this by selecting 1, and then work with timezone.now:
You are trying to add a non-nullable field 'deadline' to task without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> timezone.now
This will thus set the current timestamp to all the Tasks already in the database.
You can also decide to set it to a datetime in the future, for example 1-1-2100:
You are trying to add a non-nullable field 'deadline' to task without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> datetime.datetime(2100, 1, 1)
Another alternative is to provide the default in the model itself, but then it also will take that default for new tasks and thus not only the tasks already recorded.
Often a field is made nullable in case one does not have to specify the current timestamp. In that case you thus specify null=True, and by default it will use NULL/None as default for the exiting records:
class Task(models.Model):
# …
deadline = models.DateTimeField(null=True)
# …
If deadline is added afterwards you create the model, you need to tell django that its a null able field, such that deadline can have no value. just add null=True in deadline field
class Task(models.Model):
title = models.CharField(max_length=50)
completed = models.BooleanField(default=False)
uploaded = models.DateTimeField(auto_now_add=True)
deadline = modles.DateTimeField(null=True)
def __str__(self):
return self.title

Queryset with different datasets based on obj type

I want to implement in my application something similar to instagram's local notifications.
The main problem is that local notifications can be different types. For example:
1) Simple one: "user started following you" (this is pretty straightforward)
2) More complicated: "user and +7 other liked your photo X"
The first step of solution is to GROUP all notifications by targeted_user, event_type and user_who_triggered_event. For the first case it's okay as we will have all information. For the second case we will lack some information as Count of similar notifications. Also what if I need some additional information about those +7 user's who also liked photo X?
What I've tried:
I've tried to use GROUP BY which can work for the second case but is not okay for the first. Also I tried to do 2 separate querysets which has all needed data for each type of notification. But then I'll have a problem that I cannot combine them and also cannot use pagination. Also if I'll return 2 querysets it will be hard for front-end to sort them by datetime
The first queryset:
ReceivedNotification.objects.filter(user=user)
The second one:
ReceivedNotification.objects.filter(user=user, notification__event=Notification.LIKED_PHOTO).values_list('receiver', 'notification__event')
class Notification(TimeStampedModel):
OPEN_PROFILE = 'open_profile'
OPEN_PHOTO = 'open_photo'
ACTION_CHOICES = (
(OPEN_PROFILE, _('Open profile')),
(OPEN_PHOTO, _('Open photo')),
)
LIKED_PHOTO = 'liked_photo'
NEW_FOLLOWER = 'new_follower'
EVENT_CHOICES = (
(LIKED_PHOTO, _('Liked photo')),
(NEW_FOLLOWER, _('New follower'))
)
action = models.CharField(
max_length=255,
choices=ACTION_CHOICES,
)
event = models.CharField(
max_length=255,
choices=EVENT_CHOICES,
)
title = models.CharField(
max_length=255,
)
body = models.TextField()
class Meta:
db_table = 'notifications'
For this example I have 2 notifications in data base.
Also I have ReceivedNotification model with FK to user and FK to notification and also data field(JSONField) to store actual information
which particular user will get.
Expected result is to get all local notifications for particular user is a single query.
Some notifications should have only basic info and other should have Counter of similar notifications if there are more than 3 similar and also list of Id's of that users.

Minimizing Flagging System DB Cost

I am trying to figure out a way to make a flagging system that does not require a new instance to be entered in the database (Postgres) every time a user flags a video. (extra context below/above the code) The fields I would like to have are 'Description', 'Timestamp' and 'Flag Choice'.
So I was wondering if this would work. If I make a Flag model and make 'Flag Choices' (Gore, Excessive Violence, ect.) their own Positive Integer Fields and increment the fields accordingly and then combine the id of the post, the description for why they flagged the post, and the timestamp into ONE FIELD by separating new entries by commas into a TextField (In the User Model instead of the Flag model so I know who flagged whatever post)...Will that one Text Field eventually become too big? IMPORTANT: Every time a flag is reviewed and closed, it is deleted from said field (context below)
Context: In the Flag model there will be a post_id field along with Excessive Violence Gore ect. that are Positive Integer fields which are incremented every time someone submits a flag. Then in the User model there will be ONE field which will contain something like the following.
(Commas represent the split of the fields of 'post_id', 'description' and 'timestamp' in the database)
5, "Another flag from the same user in the same TextField.", 2019:9:15
# New Entry
...
Then to get the flag from that one field, I would use a regular expression in combination with a view (that passes a specific video as an argument from a flag management page) to get the post_id, description, timestamp from the TextField (recording the positions for slicing) then after the flag status is "Closed", the function will delete that slice (Starting with the post_id, ending with the timezone, slicing at the commas)
Will this work? The end result SHOULD be... When a post gets flagged, a new Flag model is made, at the same time (if this is the first flag from the user/the first flag for the post)a 'flag_info' field is created in the user model and the post_id, description, and timestamp are entered into said field. If that same user flags another video, a new instance is created for that specific post in the flag model and the flag choice (Gore, Excessive Violence, ect.) is incremented. At the same time the post_id, description, and timestamp are appended to the same field as the following "post_id; description; timestamp," and to grab a specific flag, use a regular expression ( and further processing on the moderation page ) to parse the post_id (used to view the specific post [which will be returned in a different function]) description, and timestamp.
Forgive me if this is difficult to understand, I'm still trying to figure this idea out myself.
I haven't found anything about this through google nor any other search engine.
Flag model
class Flag(models.Model):
FLAG_CHOICES =(
('Sexually Explicit Content', 'Sexually Explicit Content'),
('Child Abuse', 'Child Abuse'), # High priority, auto send to admin, ban if fake flag
('Promotes Definition Terrorism', 'Promotes Definition Terrorism'), # High priority, auto send to admin ban if fake flag
('Gore, Self Harm, Extreme Violence', 'Gore, Self Harm, Extreme Violence'),
('Spam/Misleading/Click-Bait', 'Spam/Misleading/Click-Bait'),
('Calling For Mass Flag', 'Calling For Mass Flag'),
('Doxing', 'Doxing'),
('Animal Abuse', 'Animal Abuse'),
('Threatening Behaviour', 'Threatening Behavior'),
('Calls To Action', 'Calls To Action')
)
STATUS_OPTIONS = (
('Open', 'Open'),
('Being Reviewed', 'Being Reviewed'),
('Pending', 'Pending'),
('Closed', 'Closed'),
)
objects = models.Manager()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
# Make positive integer fields for flag_choices so we can increment count instead of making a new instance every time
sexually_explicit_content = models.PositiveIntegerField(null=True)
child_abuse = models.PositiveIntegerField(null=True)
promotes_terrorism = models.PositiveIntegerField(null=True)
gore_harm_violence = models.PositiveIntegerField(null=True)
spam_clickbait = models.PositiveIntegerField(null=True)
mass_flag = models.PositiveIntegerField(null=True)
doxing = models.PositiveIntegerField(null=True)
animal_abuse = models.PositiveIntegerField(null=True)
threating_behaviour = models.PositiveIntegerField(null=True)
calls_to_action = models.PositiveIntegerField(null=True)
sexualizing_children = models.PositiveIntegerField(null=True)
# Increment the above fields when a flag with corresponding offence is submitted
who_flagged = models.TextField(null=True) # This will allow me to get all users who flagged a specific post (split by commas, and use a function to loop through the newly formed list of user ids, then on each iteration. I would be able to grab the user model for futher operations
flagged_date = models.DateTimeField(auto_now_add=True, null=True)
flag_choices = models.CharField(choices=FLAG_CHOICES, max_length=100, null=True) # Required Choices of offences
status = models.CharField(choices=STATUS_OPTIONS, default='Open', max_length=50, null=True)
def get_rendered_html(self):
template_name = 'vids/templates/vids/moderation.html'
return render_to_string(template_name, {'object': self.content_object})
User Model or Custom User Profile model
class CustomUser(models.Model):
...
reported = models.TextField() # This will hold all the information about the users flag
# Meaning the following things will be in the same 'box' (
flag_info) in the DB... and will look like this...
" post_id = 4; description = 'There was something in the background against the rules.'; timestamp = 2019:9:25,"
Then when the same user flags another video, something like the following would be appended to the 'flag_info' field...
All of this will be one big long string.
post_id = 24; description = "There was something in the background that showed my email."; timestamp = 2019:10:25,'
# To get flag_info from a user, I would do the following in a view
def get_flag(user, post_id):
# User is going to be the the user model that we need to pull from
# post_id is so I can use regex to pull the slice
# This is really simplified since it would take a while to write the whole thing
info = user.flag_info
split = info.split(",")
for i in split:
if i[0] == post_id:
# do something with it
# Alternatively I could do this
for i in split:
new = i.split(';')
# position 0 is the post_id, position 1 is description and position 3 is timestamp...Here I would do further processsing
To keep track of who flagged what I would make a TextField in the Flag model then every time a user flags a post, their user_id gets recorded in said TextField. When we need to review the flags, I would use the 'get_flag' function after splitting 'who_flagged' by commas. Which would extract the fields I need for processing.
Since I don't have thousands of videos/users, I can't test if the field will eventually become too large.

Make a change to date fields automatically in Django

I have 2 date fields and I want to update the date automatically.
The code won't work until I update the updated field by myself.
How can I make it update itself (updated fields) automatically?
STATUS_CHOICES = (
('P', 'Paid'),
('U', 'UNPAID'),
)
status = models.CharField(
max_length=1, choices=STATUS_CHOICES)
updated = models.DateTimeField(default=datetime.now() )
expiry = models.DateTimeField(default=datetime.now() + timedelta(days=30) )
def save(self):
if(self.expiry >= self.updated):
self.status = default = "P"
else:
self.status = default = "U"
self.expiry = default=self.updated+timedelta(days=1)
super(Users, self).save()
The DateTimeField have a auto_now property, that will set the field to the current date every time the object is saved.
The main question is what event should trigger the save action. For example you can improve your view, so that every time someone visit the page, your model would update as well as desired DateTimeField. On the other hand you may want this happen on the schedule basis, so you should use something like Cron, Celery or Huey.
In your case you have two options.
Make some code that will be executed periodically that will update the status based on current date.
Do not store status in the database at all. If it can depend only from expiry field it can be a computed property
P.S.
It's not related to what you are asking but I have a suggestion for you. Never ever use the direct result from datetime.now() as default value for DateTimeField. It's misleading for you and it's not the same as what you want to achieve. datetime.now will be executed at import time (when the server starts) and will stay the same until next restart.
If you want the default value to be the exact time when the record was created then default value need to be a function (which will be executed every time) and not the result of the function.
This means that updated need to be DateTimeFiled(default=datetime.now) (without the brackets).
This logic is more like for a field called created_at or date_created and not updated but this is your logic.

django skip a field's validation

I need to skip validation of the OrderAmount field but for it to still save the invalidated data. Is there a way this can be done? I know django allows you to make your own validation, but I don't know how to make it completely skip just one field's validation.
model:
class LiquorOrder(models.Model):
pack_size = (
('1', '1'),
('2', '2'),
)
LiquorOrderID = models.AutoField(primary_key=True)
storeliquorID = models.ForeignKey(StoreLiquor)
orderID = models.ForeignKey(Order)
OrderAmount = models.PositiveSmallIntegerField('Order Amount', max_length=3, choices=pack_size)
TotalPrice = models.DecimalField('Total Price', max_digits=5, decimal_places=2)
Form:
class AddToOrderForm(forms.ModelForm):
class Meta:
model = LiquorOrder
fields = ('OrderAmount',)
For a PositiveSmallIntegerField the only validation Django does is ensure the value is indeed a positive integer within the appropriate range. If you were to skip this, you would run into problems when Django tries to write the value to your database. If you were to, say, try to write the value "marshmallows" to a DB column that's expecting an integer, the DB will throw errors and Django will turn around and throw you an IntegrityError.
If you really wanted to try, you could override the field to be CharField with required set to False (basically allowing any keyboard input):
class AddToOrderForm(forms.ModelForm):
OrderAmount = forms.CharField(required=False)
Django would then return True when you run is_valid(), but throw errors when you try to call save().
It sounds like your real issue is with your model not matching your current project requirements. If that is the case, look into migrating to a new model. The Python library South is brilliant tool for this purpose and is used heavily by the Django community. I would read up on DB migrations and see if you can come up with a solution that way.