I'm trying to understand how to create multiple instances on creation of a model in django. Eventually I want to create more than one but I'm just trying to get the signal to work at the moment. This is what I have so far that isn't working. I want it to create a duplicate of this model.
from datetime import datetime, timedelta
import django
from django.conf import settings
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
class BudgetTransaction(models.Model):
"""
Individual transaction for Budget
"""
transaction_types = [
('FI', 'Fixed Income'),
('FE', 'Fixed Expenses'),
('EC', 'Extra Cashflow'),
]
frequencies = [
('one', 'one off'),
('wk', 'weekly'),
('fort', 'fortnightly'),
('mon', 'monthly'),
('yr', 'yearly'),
('day', 'specific day'),
]
today = datetime.today().strftime('%Y-%m-%d')
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
help_text="Owner of the item"
)
transaction_type = models.CharField(max_length=40, choices=transaction_types, default=1)
transaction_name = models.CharField(max_length=100, null=False)
transaction_amount = models.IntegerField(null=False)
next_date = models.DateField(null=False, default=today)
frequency = models.CharField(max_length=20, choices=frequencies, default=1)
complete = models.BooleanField(default=False)
def __str__(self):
return self.transaction_name
class Meta:
ordering = ['next_date']
#receiver(post_save, sender=BudgetTransaction)
def create_forecasted(sender, instance, created, **kwargs):
if created:
today = datetime.today()
this_month = today.month
months_left = 12 - this_month
if sender.frequency == "mon":
BudgetTransaction.objects.create(owner=instance.owner)
Thanks,
Mitchell
I suggest to create a file called signals.py in the app next to models.py and place all your signals there as a cleaner structure I mean.
The #receiver decorator listens to the requests made to the server. If you make a viewset for this model with an endpoint to create a BudgetTransaction model. the receiver will work perfectly. But if this is done from django admin, I think the receiver won't work as you mentioned.
I think this may work here instead of #receiver. post_save.connect() as here https://docs.djangoproject.com/en/4.0/ref/signals/
I solved this by using bulk_create rather than just create. I've put an example below to illustrate my change. It was receiving the request correctly with my original post however the code was just not functioning correctly once it was received. I could be wrong and I hope someone corrects this if it is wrong but I was initially getting a recursive error while using create() which I believe is because every time it ran it would resend a signal to create another item.
BudgetTransaction.objects.bulk_create([BudgetTransaction(
owner=instance.owner,
transaction_amount=1000,
transaction_name=instance.transaction_name,
transaction_type=instance.transaction_type,
next_date="2022-08-08",
)])
Related
I want to make a TextField in my model that store every changes of another field. That field to store data like an array. How can I do that? Can anybody give an advice?
exemple:
class Exemple(models.Model):
field = models.ForeignKey(AnotherModel, blank=True)
history_field = models.TextField(blank=True) # => ['old_field_value', 'field_value']
When "field" receive a value I want to append that value to "history_field"
SOLUTION**
pip install django-mysql
from django.db.models.signals import post_save
from django.dispatch import receiver
from django_mysql.models import ListTextField
class Exemple(models.Model):
field = models.ForeignKey(AnotherModel, blank=True)
history_field = ListTextField(
base_field=models.TextField(),
size=150,
blank=True
)
#receiver(post_save, sender=Exemple)
def update_history_field(sender, instance, **kwargs):
if instance.field:
data = Exemple.objects.get(pk=instance.id)
history = data.history_field
history.append(instance.field.name) #field is foreignkey
Exemple.objects.filter(pk=instance.id).update(history_field=history)
#result history_field=['data1', 'data2', 'data3']
This is the solution that I`ve made
from django.contrib.auth.models import User
from django.db.models.signals import post_save, m2m_changed
from django.dispatch import receiver
# Create your models here.
class FollowersModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1, related_name='usr')
follow = models.ManyToManyField(User, related_name="whom")
my_followers = models.ManyToManyField(User, related_name='my_followers')
update = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return str(self.user)
#receiver(m2m_changed, sender=FollowersModel.follow.through)
def follow_by_follow_add_or_remove(sender, **kwargs):
if kwargs.get('action') == 'post_add':
print kwargs.get('instance').follow.all()
**solved part**
print list(kwargs.get('pk_set'))[0]
# then it returns the id of the added object :)))
In this example when I print kwargs.get('instance').follow.all() then I can get the whole lists of follow label but I just want to get only the added one, I mean I am searching kind of print kwargs.get('instance').follow.this(), just like jquery we do 'this.append()' only the added one
Okey Bros It is Solved
solved part is in the codes :))
From the documentation of m2m_changed signal:
pk_set
For the pre_add, post_add, pre_remove and post_remove actions, this is a set of primary key values that have been added to or removed from the relation
You can access pk_set from the kwargs to check which primary keys values have been added/removed.
You can use this pk_set to fetch the corresponding objects from database.
I'm trying to create a manager that has a method 'active_or_users' to retrieve all accounts that are active, or that an user has created. An active account has a start date that is either today, or somewhere in the past, and a end date that is somewhere in the future. Right now the active_or_users method works, however it returns duplicates of the same object. It's returning three copies of a user created active job. This is less than ideal.
from django.db.models import Q
from django.db import models
from django.contrib.auth.models import User
class ActiveJobs(models.Manager):
def active(self):
return super(ActiveJobs, self).get_query_set().\
filter(publications__publish_on__lte=date.today(),
publications__end_on__gt=date.today())
def active_or_users(self, user):
return super(ActiveJobs, self).get_query_set().\
filter((Q(publications__publish_on__lte=date.today()) &
Q(publications__end_on__gt=date.today())) | Q(creator=user))
class Job(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(blank=True, null=True)
creator = models.ForeignKey(User)
objects = ActiveJobs()
class JobPublicationRecord(models.Model):
job = models.ForeignKey('Job', related_name='publications')
publish_on = models.DateField(auto_now=False)
end_on = models.DateField(auto_now=False, auto_now_add=False,
blank=True, null=True)
To put the comments into an answer
With the OR query, an instance will be returned for every hit of the query. I.e: an instance if a Job is created by user and another instance of the same job if also in the date range specified, etc.
So to fix this, change the method active_or_users to:
def active_or_users(self, user):
return super(ActiveJobs, self).get_query_set().filter(
(Q(publications__publish_on__lte=date.today()) &
Q(publications__end_on__gt=date.today())) | Q(creator=user)).distinct()
I have an app that I'm calling Progress. I want to have several different apps throughout my project write to it whenever something happens. But all the different apps are not the same in how they broadcast progress. So I thought that a ContentType solution would work.
The only trick that I'm having a hard time figuring out is that I need to write to the Progress app when an event occurs. Such as when a view renders. I've been trying get_or_create but I'm having trouble getting the right configuration in the queryset. Any suggestions for how to correct this?
I want the get_or_create to sit in the view of an app so that the action I want is what writes to the progress.
My Progress Model.py
from django.db import models
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from datetime import datetime
class Progress(models.Model):
"""
The page log. Records the user and the object.
"""
user = models.ForeignKey(User, related_name='user_pagelog')
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
stamp = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = (('user', 'content_type', 'stamp'),)
verbose_name = 'View Log'
verbose_name_plural = 'View Logs'
get_latest_by = 'stamp'
def __str__(self):
return "%s got to %s on %s" % (self.user, self.content_type, self.stamp)
#classmethod
def get_latest_view(cls, user):
"""
Get most recent view log value for a given user.
"""
try:
view_log = cls.objects.filter(user=user).order_by('-stamp')[0]
return view_log.value
except IndexError:
return None
An example of the queryset that I want to write to the Progress app:
Progress.objects.get_or_create(user=request.user, content_type=f.id)
Where f = get_object_or_404(Page, publish=True)
Finally, the error I'm getting:
Cannot assign "1": "Progress.content_type" must be a "ContentType" instance.
Which I think it means the instance isn't getting found? But it exists, so I'm confused.
Thanks.
No, it doesn't mean that at all. It means what it says, that the parameter has to be an instance of the ContentType model, whereas you're passing the ID of the object itself.
You might be able to use content_type along with the actual instance:
Progress.objects.get_or_create(user=request.user, content_object=f)
Otherwise you'll need to get the right ContentType using the get_for_model() method, and pass that along with the object id.
I'm trying to use social_auth (omab) for the first time and I'm find that there is no working example how to store basic facebook user data. Authentication works and user is created without problem as it's explained in the social_auth docs but I need to store gender and locale also. Both of them belongs to the basic facebook user data so they are in the facebook response all the time.
I'm use Django 1.4, Python2.7 and latest social_auth. So I was try to use SOCIAL_AUTH_USER_MODEL = 'myapp.UserProfile' in settings.py file and model.py is:
#!/usr/bin/python
#-*- coding: UTF-8 -*-
from django.db import models
from django.contrib.auth.models import User
from django.db.models import signals
import datetime
from datetime import timedelta
from django.db.models.signals import post_save
from social_auth.signals import pre_update
from social_auth.backends.facebook import FacebookBackend
class CustomUserManager(models.Manager):
def create_user(self, username, email):
return self.model._default_manager.create(username=username)
class UserProfile(models.Model):
gender = models.CharField(max_length=150, blank=True)
locale = models.CharField(max_length=150, blank=True)
#social_auth requirements
username = models.CharField(max_length=150)
last_login = models.DateTimeField(blank=True)
is_active = models.BooleanField()
objects = CustomUserManager()
class Meta:
verbose_name_plural = 'Profiles'
def __unicode__(self):
return self.username
def get_absolute_url(self):
return '/profiles/%s/' % self.id
def facebook_extra_values(sender, user,response, details, **kwargs):
profile = user.get_profile()
current_user = user
profile, new = UserProfile.objects.get_or_create(user=current_user)
profile.gender = response.get('gender')
profile.locale = response.get('locale')
profile.save()
return True
pre_update.connect(facebook_extra_values, sender=FacebookBackend, weak = False, dispatch_uid = 'facebook_extra_values_user')
In the settings.py I'm define pipeline
SOCIAL_AUTH_PIPELINE = (
'social_auth.backends.pipeline.social.social_auth_user',
#'social_auth.backends.pipeline.associate.associate_by_email',
'social_auth.backends.pipeline.user.create_user',
'social_auth.backends.pipeline.social.associate_user',
'social_auth.backends.pipeline.social.load_extra_data',
'social_auth.backends.pipeline.user.update_user_details',
'social_auth.backends.pipeline.misc.save_status_to_session',
)
but with above I get error AssertionError: ForeignKey(None) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string 'self'
Also I was try to use AUTH_PROFILE_MODULE = 'myapp.UserProfile' instead as I was do before to extend user.model, which works well but don't understand how to populate needed data when UserProfile is created. Does anyone can place working code for this problem?
Thanks
There are a couple of ways to archive it, what fits better to your project is up to you of course, here's a list of available options:
Define this setting FACEBOOK_EXTRA_DATA = ('gender', 'locale'), the values will be available at the UserSocialAuth instance, to get them just do user.social_auth.get(provider='facebook').extra_data['gender'] or ['locale']. This is possible just because the information is available in the basic user data response.
Use a user profile to store this data (check django doc about it). Then just add a pipeline entry that stores the values in your profile instance.
Create a custom user model, SOCIAL_AUTH_USER_MODEL = 'myapp.CustomUser', and again add a custom pipeline entry that stores the values in your user instance.
Number 1 is not the best solution IMO, since a user can have several Facebook accounts connected and it could create a mess. Number 2 is good for Django 1.4 and lower, but it's deprecated starting from Django 1.5, something to take into account. Number 3 is a bit messy IMO.