Django fake model instanciation - No testunit [duplicate] - python

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)

Related

How to automatically fill-in model fields in Django rest_framework serializer?

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

How to control field name for related model fetching in Peewee?

I have two models: User and Message. Each Message has two references to User (as sender and as receiver). Also I have defined an other_user hybrid method for Message which returns "user other than specific one" - see below:
from peewee import *
from playhouse.hybrid import hybrid_method
from playhouse.shortcuts import case
class User(Model):
name = CharField()
class Message(Model):
sender = ForeignKeyField(User, related_name='messages_sent')
receiver = ForeignKeyField(User, related_name='messages_received')
text = TextField()
#hybrid_method
def other_user(self, user):
if user == self.sender:
return self.receiver
elif user == self.receiver:
return self.sender
else:
raise ValueError
#other_user.expression
def other_user(cls, user):
return case(user.id, (
(cls.sender, cls.receiver),
(cls.receiver, cls.sender)))
Now I want to make a composite query which will retrieve all messages for current user and also retrieve information about "other" user than current. Here is how I do it:
current_user = request.user # don't matter how I retrieve it
query = (Message.select(Message, User)
.where(
(Message.sender == current_user) |
(Message.receiver == current_user))
.join(User, on=(User.id == Message.other_user(current_user))))
This query works well - i.e. it retrieves the exact information I need.
But here is the problem: "other user" information is always saved as sender field.
If I use this with models which have no direct ForeignKey reference then peewee creates a new field (in this case it would be named user) for additional requested model. But if there is at least one ForeignKey relationship from primary model to secondary requested model then it uses first such relationship.
Is it possible to somehow override this behaviour?
I tried Model.alias() method, but it (unlike Node.alias) doesn't allow to specify name.
I'm not completely sure what you want, so I'll provide a snippet that will likely work for your specific scenario, and will hopefully also allow you to learn how to do what you want, if this isn't it:
SenderUser = User.alias()
ReceiverUser = User.alias()
OtherUser = User.alias()
query = (Message.select(Message, SenderUser, ReceiverUser)
.where(
(Message.sender == current_user) |
(Message.receiver == current_user))
.join(SenderUser, on = (Message.sender == SenderUser.id).alias('sender'))
.switch(Message)
.join(ReceiverUser, on = (Message.receiver == ReceiverUser.id).alias('receiver'))
.switch(Message)
.join(OtherUser, on = (Message.other_user(current_user) == OtherUser.id).alias('other_user'))
Notes:
You don't really need to create all those aliases (SenderUser/ReceiverUser/OtherUser), just two, and use User for the other. I just find that the query becomes more readable like this.
When you define an alias in the on clause, you basically tell peewee in which variable to store the joined table. I'm sending them directly to the already existing properties (sender/receiver). Also, I'm creating an extra property in the model with the value of the other user, which you can access as usual with self.other_user.
That switch method switches the current context to Message, so you can join a new table to Message instead of the SenderUser/ReceiverUser contexts where you end up after the two first joins.
If for some reason you're joining something that might be undefined (which doesn't seem to be the case here as both users are likely mandatory), you would probably want to add that you want a left outer join, like this:
.join(ReceiverUser, JOIN.LEFT_OUTER, on = (Message.receiver == ReceiverUser.id).alias('receiver'))
Don't forget to from peewee import JOIN
Something else I just noticed, is that you likely want to change that other_user method you have to compare ids instead of the model variables. If self.sender is not filled when you access it, peewee will trigger a database select to get it, so your other_user method possibly triggers 2 select queries. I would do it like:
#hybrid_method
def other_user_id(self, user):
if user.id == self.sender_id:
return self.receiver_id
elif user.id == self.receiver_id:
return self.sender_id
else:
raise ValueError
You can see that I use sender_id instead of sender.id. That uses the ids for each foreign key that are already set in the message model. If you did self.receiver.id you would likely trigger that select anyway, to then access the id property (I'm not 100% sure here though).

Fetch and save a copy of a QuerySet with a new User field in Django

I have a number of models, each with a user field. Each time a new user is registered I want to copy the instances of these models which have user.id = 1, and save them with a new user id in the user field. I've read that a copy of a model instance can be made by setting the pk to None so this is what I've tried to do first. I'm trying to do this on a QuerySet since there are many instances for some tables (over 1000 for some models, though mostly less than 100).
def copy_tables(user):
# This approach fails with "Voice has no field named 'pk'"
voice = Voice.objects.filter(user=1).update(
pk=None, user=user.id, right_answers=0, wrong_answers=0)
I've also tried using copy.deepcopy(Voice.objects.filter(user=1)) but calling update() on this is also still changing the original table in the database.
def copy_tables(user):
copy.deepcopy(Voice.objects.filter(user=1)).update(
user=user.id, right_answers=0, wrong_answers=0)
# This test throws an error because the original models have been changed
assert Voice.objects.filter(user=1).first()
Am I missing some other way of doing this? Also, I thought that all model instances had a primary key, pk, which is automatically set by Django. Is this not the case?
When calling the save method on a model instance, Django determines if it must be inserted or updated by checking if it has a primary key (ref.).
So if you set the PK to None on a model instance, Django will trying adding it as a new row in the database.
Demo:
# Retrieve the object belonging to the user whose ID is 1.
# Note that you cannot use "user=1", you need to tell Django you
# are querying by id, using "user__id=1" .
obj = MyModel.objects.get(user__id=1)
# Set PK to None so that Django thinks it is not save in the database.
obj.pk = None
# Save to insert it.
obj.save()
In your case where you have multiple objects, you can use bulk_create to gain in performances:
voices = Voice.objects.filter(user__id=1)
new_voices = []
for voice in voices:
voice.pk = None
voice.user = user
right_answsers = 0
wrong_anwsers = 0
new_voices.append(voice)
Voice.objects.bulk_create(new_voices)

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/

Google App Engine Python Datastore

Basically what Im trying to make is a data structure where it has the users name, id, and datejoined. Then i want a "sub-structure" where it has the users "text" and the date it was modified. and the user will have multiple instances of this text.
class User(db.Model):
ID = db.IntegerProperty()
name = db.StringProperty()
datejoined = db.DateTimeProperty(auto_now_add=True)
class Content(db.Model):
text = db.StringProperty()
datemod= db.DateTimeProperty(auto_now_add = True)
Is the code set up correctly?
One problem you will have is that making User.ID unique will be non-trivial. The problem is that two writes to the database could occur on different shards, both check at about the same time for existing entries that match the uniqueness constraint and find none, then both create identical entries (with regard to the unique property) and then you have an invalid database state. To solve this, appengine provides a means of ensuring that certain datastore entities are always placed on the same physical machine.
To do this, you make use of the entity keys to tell google how to organize the entities. Lets assume you want the username to be unique. Change User to look like this:
class User(db.Model):
datejoined = db.DateTimeProperty(auto_now_add=True)
Yes, that's really it. There's no username since that's going to be used in the key, so it doesn't need to appear separately. If you like, you can do this...
class User(db.Model):
datejoined = db.DateTimeProperty(auto_now_add=True)
#property
def name(self):
return self.key().name()
To create an instance of a User, you now need to do something a little different, you need to specify a key_name in the init method.
someuser = User(key_name='john_doe')
...
someuser.save()
Well, really you want to make sure that users don't overwrite each other, so you need to wrap the user creation in a transaction. First define a function that does the neccesary check:
def create_user(username):
checkeduser = User.get_by_key_name(username)
if checkeduser is not None:
raise db.Rollback, 'User already exists!'
newuser = User(key_name=username)
# more code
newuser.put()
Then, invoke it in this way
db.run_in_transaction(create_user, 'john_doe')
To find a user, you just do this:
someuser = User.get_by_key_name('john_doe')
Next, you need some way to associate the content to its user, and visa versa. One solution is to put the content into the same entity group as the user by declaring the user as a parent of the content. To do this, you don't need to change the content at all, but you create it a little differently (much like you did with User):
somecontent = Content(parent=User.get_by_key_name('john_doe'))
So, given a content item, you can look up the user by examining its key:
someuser = User.get(somecontent.key().parent())
Going in reverse, looking up all of the content for a particular user is only a little trickier.
allcontent = Content.gql('where ancestor is :user', user=someuser).fetch(10)
Yes, and if you need more documentation, you can check here for database types and here for more info about your model classes.
An alternative solution you may see is using referenceproperty.
class User(db.Model):
name = db.StringProperty()
datejoined = db.DateTimeProperty(auto_now_add=True)
class Content(db.Model):
user = db.ReferenceProperty(User,collection_name='matched_content')
text = db.StringProperty()
datemod= db.DateTimeProperty(auto_now_add = True)
content = db.get(content_key)
user_name = content.user.name
#looking up all of the content for a particular user
user_content = content.user.matched_content
#create new content for a user
new_content = Content(reference=content.user)

Categories