I am having some troubles with Django's save() method.
1 / I have this simple Model :
class User (models.Model):
userId = models.IntegerField()
appInstance = models.TextField(null=True, blank=True)
2 / Then in a view, I check if the appInstance does exist and if not I call a function:
if not u.appInstance:
instance = autoAddApplication(request)
3 / and autoAddApplication is defined as follows:
def autoAddApplication(request):
session = request.session
user = get_object_or_404(User, userId = session['user_id'])
## do stuff here and end up with an 'instanceMap' dictionary
user.appInstance = simplejson.dumps(instanceMap)
user.save()
return instanceMap
The code runs with no error, but I don't get the Model saved in the database when the autoAddApplication function is called from the condition in step 2.
I tried to call this 'autoAddApplication' function directly by mapping a URL diretly to this function and then it does work and I get my Model saved in the DB.
I'm completely puzzled here. Why doesn't it work when I call this 'autoAddApplication' function from within another function ? Any help would be greatly appreciated.
EDIT
I finally found what I was doing wrong.
Later in step 2 I had a u.save() which was indeed saving u (and therefore overridding the changes I made in the autoAddApplication function).
I solved it by passing along u to the autoAddApplication function.
Anyway thanks for your help.
Try changing the class name from User to MyUser or something like that, you may be having troubles with the User model defined in Django.
How about to use autoAddApplication as a private function inside the view?
def _auto_add_application(session):
user = get_object_or_404(User, user_id = session['user_id'])
user.app_instance = simplejson.dumps(instance_map)
user.save()
return instance_map
Related
This question already has an answer here:
How can I create a django model instance with deferred fields without hitting the database?
(1 answer)
Closed 8 months ago.
I want to know if I can instanciate an empty fake model just with id of database record.
I found way to create mockup model, but I want a production-friendly solution.
Explanation of my issue :
I want to list users settings for users who choose to be displayed on public mode :
user_displayed_list = UserPublicProfile.objects.filter(
displayed = True,
).only(
'user_id',
'is_premium',
)
user_settings_list = []
for user_displayed in user_displayed_list:
# I have to send user Instance to the next method :
user_settings = self.get_user_settings(user_displayed.user)
user_settings_list.append(user_settings)
# But ’user_displayed.user’ run an new SQL query
I know I can improve my queryset as :
user_displayed_list = UserPublicProfile.objects.filter(
displayed = True,
).select_related(
'user'
).only(
'user',
'is_premium',
)
But It makes an useless join because I need only the user id field in get_user_settings():
The get_user_settings() method (it could help to understand context):
def get_user_settings(self, user)
user_settings = UserSettings.objects.get(user = user)
return user_settings
In real project, this method run more business feature
Is there a way to instanciate a User model instance with only id field filled ?
I don't want to use a custom empty class coded for this purpose. I really want an object User.
I didn't find anything for that. If it's possible, I could use it by this way :
for user_displayed in user_displayed_list:
FakeUser = User.objects.create_fake(id = user_displayed.user_id)
# I have to send user Instance to the next method :
user_settings = self.get_user_settings(FakeUser)
Without seeing the complete models, I'm assuming a bit. Assuming that UserSettings has a ForeignKey to User. Same for UserPublicProfile. Or User has ForeignKey to UserSettings. Works as well.
Assuming that, I see two solutions.
Solution #1; use the ORM to full potential
Just saw your comment about the 'legacy method, used many times'.
Django relations are very smart. They accept either the object or the ID of a ForeignKey.
You'd imagine this only works with a User. But if you pass the id, Django ORM will help you out.
def get_user_settings(self, user)
user_settings = UserSettings.objects.get(user = user)
return user_settings
So in reality, these work the same:
UserSettings.objects.get(user=1)
UserSettings.objects.get(user_id=1)
Which means this should work, without a extra query:
user_displayed_list = UserPublicProfile.objects.filter(
displayed = True,
).only(
'user_id',
'is_premium',
)
user_settings_list = []
for user_displayed in user_displayed_list:
# I have to send user Instance to the next method :
user_settings = self.get_user_settings(user_displayed.user_id) # pass the user_id instead of the object.
user_settings_list.append(user_settings)
Solution #2: chain relations
Another solution, again, still assuming quite a bit ;)
It would think you can chain the model together.
Assuming these FK exists: UserPublicProfile -> User -> UserSetting.
You could do this:
user_displayed_list = UserPublicProfile.objects.filter(
displayed = True,
).select_related(
'user', 'user__usersettings', # depends on naming of relations
).only(
'user',
'is_premium',
)
for user_displayed in user_displayed_list:
# I have to send user Instance to the next method :
user_settings = user_displayed.user.usersettings # joined, so should cause no extra queries. Depends on naming of relations.
user_settings_list.append(user_settings)
Let's assume I have a model like this:
class Data(models.Model):
a = models.CharField()
b = models.CharField()
c = models.IntegerField()
I would like to setup a serializer in such a way that it automatically fills in field c and it is not required for a POST. I tried to overwrite the create function of the serializer, but it doesn't work:
class DataSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Data
fields = ('a', 'b')
def create(self, validated_data, **kwargs):
Data.objects.c = 5
return Data.objects.create(**validated_data)
However, if I try this, I end up with an IntegrityError: NOT NULL constraint failed: model_data.c. What is the syntax that I have to use here?
EDIT: Updated formatting.
The reason you're getting the error because field c is not set to null = True - as such an error is raised at the validation stage even before the serializer hits the create method.
Bear in mind that the process goes like this:
Submit serializer data
field-level validation happens - this includes checks for null integrity, min/max length etc and also any custom field validations defined in def validate_<field_name>
object-level validation happens - this calls the def validate method
validated data is passed to the save method, depending on how you designed the serializer - it will save the instance, or route the data to either create or update
All of the info regarding this can be found in Django's and DRF's docs.
A few things to consider:
are you setting a global default for that field? If so, set the default in your models - c = models.IntegerField(default=a_number_or_a_callable_that_returns_an_integer)
do you intend to display the field? If so, include c in your fields and add one more Meta attribute - read_only_fields = ('c',)
If it's neither of the above, you might want to override the validate_c method
Apologies for the poor formatting, typing it on my phone - will update once I get to a computer
In your code Data.objects.c = 5 does nothing.
If you want to set this value yourself use validated_data['c'] = 5 or Data.objects.create(c=5, **validated_data) (just not both at the same time).
Rather than doing this in the serializer, there are hooks in the generic views that allow you to pass values to the serializer. So in your case you might have:
class DataViewSet(ModelViewSet):
# ...
def perform_create(self, serializer):
serializer.save(c=5)
See the "Save and deletion hooks" section here
Can Django's user_passes_test() access view parameters?
For example I have view that receives an id to retrieve specific record:
def property(request, id):
property = Property.objects.get(id=int(id))
The record has a field named user_id that contains the id for user that originally created record. I want users to be able to view only their own records otherwise be redirected.
I'd like to use a custom decorator which seems simple and clean.
For custom decorator is there some variation of something like this that will work?
#user_passes_test(request.user.id = Property.objects.get(id=int(id)).id, login_url='/index/')
def property(request, id):
property = Property.objects.get(id=int(id))
I have tried creating separate test_func named user_is_property_owner to contain logic to compare current user to property record user_id
#user_passes_test(user_is_property_owner(id), login_url='/index/')
def property(request, id):
property = Property.objects.get(id=int(id))
def user_is_property_owner(property_id):
is_owner = False
try:
Property.objects.filter(id=property_id, user_id=user_id).exists()
is_owner = True
except Property.DoesNotExist:
pass
But having trouble getting current user id and the property id from the request into the user_is_property_owner decorator function.
EDIT to add solution I was using. I did test inside each view test was required. It is simple. i thought using a decorator might be prettier and slightly more simple.
def property(request, id):
# get object
property = Property.objects.get(id=int(id))
# test if request user is not user id on property record
if request.user.id != property.user_id:
# user is not same as property user id so redirect to index
return redirect('index')
# rest of the code if request user is user_id on property record
# eg it is ok to let user into view
Typically, (using class based views), I'll handle this in the get_queryset method so it would be something like
class PropertyDetail(DetailView):
def get_queryset(self):
return self.request.user.property_set.all()
and that will give a 404 if the property isn't for the current user. You might prefer to use a project like django-guardian if you end up with more permission relationships than just Property.
If you take a look at UserPassesTestMixin you'll see that it processes the test_func before calling dispatch so you'll have to call self.get_object(request) yourself if you decide to go that route.
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/
I just started with Django and thought maybe someone could enlighten me about a simple test I wrote that should pass but obviously doesn't. Well, I say it should but being pretty new to Django I guess my lack of knowledge is the reason it fails.
I extended the django User model with another model and a one to one relationship, as suggested in the docs, to add a field to the model. That model, namely UserStep, simply contains an Integer that starts at zero. I decided to extend the manager too to implement a method named setUserStep that set the step to a new value if the condition is met.
models.py
class UserStepManager(models.Manager):
def setUserStep(self, user, conditional_step, step):
if user.userstep.step == conditional_step:
user.userstep.step = step
user.userstep.save()
class UserStep(models.Model):
"""This model holds the user step count"""
user = models.OneToOneField(User)
step = models.IntegerField(default=0)
# we set the custom manager
objects = UserStepManager()
Then when the User signs up, a new UserStep object gets created.
models.py
#receiver(user_signed_up)
def complete_social_signup(sender, **kwargs):
"""We start step to 0"""
user = kwargs.pop('user')
us = UserStep(user=user)
us.save()
I use the method setUserStep defined in the manager in one of my views that gets called when a user requests /phase1/step1/ URL. So, if the current step is zero, set it to 1. As simple as that.
views.py
#login_required
def step1(request):
context_dict = {}
UserStep.objects.setUserStep(request.user, 0, 1)
return render(request, 'phase1/step1.html', context_dict)
I tested that behavior by hand by signing up and calling that view, and everything worked as expected. When I check the database, user.userstep.step is set to 1.
Then, being a good boy, I decided I would start writing some tests (I'm not at the point where I write the tests first yet, I should write tests to test my tests before that :P).
tests.py
class MyTests(TestCase):
def setUp(self):
self.test_user = User.objects.create_user(username="test",
email="test#test.com",
password="test")
# Mock user_signed_up signal.
us = UserStep(user=self.test_user)
us.save()
self.c = Client()
def test_step_gets_incremented(self):
self.c.login(username="test", password="test")
response = self.c.get('/phase1/step1/')
# user is logged in, the test should pass
self.assertEqual(response.status_code, 200)
current_user_step = self.test_user.userstep.step
# Test doesn't pass. WHY ME
self.assertEqual(current_user_step, 1)
AND BOOM AssertionError: 0 != 1
I'm even doing a simple print in my view method and it gets called as it should.
Thanks!
You have to reload test_user from the database since changing of the user is performed to another user instance. That means if:
u1 = User.objects.get(id = 1)
u2 = User.objects.get(id = 1)
changes to u1 (even if saved) are not mirrored to u2. So you have to get again the user from the db after the request.