I want to make friend system that have db model like this:
class Users(ndb.Model):
username = ndb.StringProperty(required = True)
bio = ndb.StringProperty()
class friend_list(ndb.Model):
list = ndb.StringProperty(repeated=True)
class friend_pending(ndb.Model):
list = ndb.StringProperty(repeated=True)
friend_pending is model for friend that not yet accepted. While friend_list is model for friend that are accepted.
I want to make both friend_list and friend_pending to be child of Users entity. Is it possible?
Here's the second approach if it is not possible:
class Users(ndb.Model):
username = ndb.StringProperty(required = True)
bio = ndb.StringProperty()
class friend_list(ndb.Model):
user_username = ndb.StringProperty(required = True)
list = ndb.StringProperty(repeated=True)
class friend_pending(ndb.Model):
user_username = ndb.StringProperty(required = True)
list = ndb.StringProperty(repeated=True)
If both are possible, which are better for cost and performance?
I want to make both friend_list and friend_pending to be child of Users entity. Is it possible?
Yes. When you create an entity, you can use the "parent" parameter to designate a parent (or parents) for the entity.
Google's Entity Keys section covers this well.
Example:
#Create User entity
#This code assumes you're using GAE's built-in user's class
user = users.User("Albert.Johnson#example.com")
user.put()
#Create a friend list and set its parent to the user we create above
friend_list = Friend_List(parent=user)
#Save to datastore
friend_list.put()
Keep in mind that the Users class in GAE is specially defined and has additional functions that you need to acknowledge. See the documentation here.
If both are possible, which are better for cost and performance?
I can't say for sure because I don't know exactly how you will be using these models, but in most(maybe all) cases your first approach would be more efficient.
Lastly, the correct naming convention for Datastore models is to capitalize the first letter. For example, your friend list class should be "Friend_List".
Related
i am trying to do permission for user where i defined my model like this
class UserInstances(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,blank=True,null=True,unique=False)
instance_name = models.CharField(max_length=150)
instance_size = models.PositiveIntegerField(default=0)
instance_objects = models.PositiveIntegerField(default=0)
instance_map = models.TextField()
instance_permission = models.ManyToManyField(User,related_name='instance_permission')
def __str__(self):
return f"{self.instance_name} instance for {self.user}"
API code
user = Token.objects.get(key=request.auth)
if UserInstances.objects.get(id=2).instance_permission.exists(user) :// for demonstration
print("access granted")
Can you tell me how to check if user exist or not in many to many field object
you can use
.values_list('{your field}', flat=True)
and then use
in python methode
or you can append users id in a list and then use
in python methode like this:
users = UserInstances.objects.get(id=2).instance_permission.all()
usres_list_id = []
for user in users:
usres_list_id.append(user.id)
if user_instance.id in users_id_list:
#do somethong
Many-to-many relationships can be queried using lookups across relationships, please refer to the Django documentation here.
Long story short, you can filter the UserInstances model and query for the users in instance_permission, i.e. using instance_permission__in.
Is it possible to assign a dynamic Entity Kind to an Expando Model? For example, I want to use this model for many types of dynamic entities:
class Dynamic(ndb.Expando):
"""
Handles all "Post types", such as Pages, Posts, Users, Products, etc...
"""
col = ndb.StringProperty()
parent = ndb.IntegerProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
Right now I use the "col" StringProperty to hold the Kind (like "Pages", "Posts", etc) and query for the "col" every time.
After reading the docs, I stumbled upon this #classmethod:
class MyModel(ndb.Model):
#classmethod
def _get_kind(cls):
return 'AnotherKind'
Does that mean I can do this?
class Dynamic(ndb.Expando):
"""
Handles all "Post types", such as Pages, Posts, Users, Products, etc...
"""
col = ndb.StringProperty()
parent = ndb.IntegerProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
#classmethod
def _get_kind(cls):
return 'AnotherKind'
But how do I dynamically replace 'AnotherKind'? Can I do something like return col?
Thanks!
I don't know if you can do that, but it sounds dangerous, and GAE updates might break your code.
Using subclasses seems like a much safer alternative. Something like this:
class Dynamic(ndb.Expando):
parent = ndb.IntegerProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
class Pages(Dynamic):
pass
class Posts(Dynamic):
pass
class Users(Dynamic):
pass
You could also try using PolyModel.
We need to know more about your application and what you are trying to accomplish to give more specific advice.
I have a logical "how to" or best practices problem.
A simplified example
I have a model class with an textbox. Users can add new entries but they will only be displayed for other users if the admin accepts them.
class MyClass(models.Model):
# Relation to a user
user = ForeignKey(User)
# Simple textbox as example attribute
text = TextArea()
# Admin has to accept the entry that other users can see it
accepted = BooleanField(default=False)
Problem
I would like to let users modify a listing, but the admin has to accept them first. As long as the admin hasn't accept the modification it should still show the old unmodified version of the entry.
My Approaches
a) create a new class
class MyEditClass(models.Model)
# ForeignKey to the original class
fk = ForeignKey(MyClass)
user = ForeignKey(User)
text = TextArea()
accepted = BooleanField(default=False)
The modifications gets saved in a new table/class. If the admin accepts this modified entry the original entry becomes this one.
Why I don't like it? My class has about 60 attributes with a lot of relations. As I haven't found a solution to duplicate a complete class this produces a lot of duplicated lines of code. If I add a new attr. in MyClass I also have to add it in MyEditClass ...
b) Add a new atrribute to MyClass if entry got edited
class MyClass(models.Model):
# new attribute with the primary key of the edited field
edited_pk = PositiveIntegerField(default=None, blank=True, none=True)
user = ForeignKey(User)
text = TextArea()
accepted = BooleanField(default=False)
In this case you don't create a new class, instead you save the edited entries in the same class and add the attribute edited_pk. If a entry is a new entry set edited_pk = None (default). If a User modifies an entry get the pk from the original entry. Then add the modified one as a new entry with edited_pk = original_entry.pk. If the admin accepts the modified version, the original entry gets overridden with the modified one.
Why I don't like this solution? As Admin I would like to have a single class in the backend to accept modified entries.
Do you have any other (maybe already bultin or 3rd party) approaches?
Thanks in advance
(Title is bad, but I can't find a better name. Please edit if you do so)
Solution
If you don't want to use a 3rd Party app check the marked answer.
My favorite solution, also by Obj3ctiv3_C_88 needs django-simple-history.
Therefore I created a method:
class MyClass(models.Model):
user = ForeignKey(User)
text = TextArea()
accepted = BooleanField(default=False)
history = HistoricalRecords() # rtd from django-simple-history
def get_accepted(self):
"""Return the first entry from the history which is accepted."""
return self.history.filter(accepted=True).first()
in your views:
# This code may be optimized, but for now it works
items = MyClass.objects.all()
items = list(items) # convert queryset to a list
i = 0
for item in items:
# Important to get the instance. Otherwise custom methods won't work
items[i] = item.get_accepted().instance
i += 1
Would something like this work?
class BlogComment(models.Model):
blog = models.ForeignKey(Blog)
user = models.ForeignKey(User)
unapproved = models.Charfield(max_length=1000, default=None, Blank=True)
approved = models.Charfield(max_length=1000, default=None, Blank=True)
# on submit
BlogComment.unapproved = request.POST['user_comment']
# on approve
BlogComment.approved = BlogComment.unapproved
BlogComment.unapproved = None
BlogComment.save()
This would allow you to keep 2 distinct states for the same comment. You only render the BlogComment.approved. For find the comments which need approval you just filter(~Q(unapproved = None))
How do I create a copy of a Django model instance along with a related One-to-One field?
Copying the model instance works fine, but when I try to create a copy of the one-to-one model, all the fields of that model become blank. Here's what I did:
new_address = self.object.designer.address # type Address
new_address.pk = None
new_address.save()
new_contact = self.object.designer # type Contact
new_contact.pk = None
new_contact.address = new_address
new_contact.save()
self.object.shippinginfo.contact = new_contact
self.object.shippinginfo.save()
The Contact model has a one-to-one relationship with the Address model. I tried printing out the values, after creating the new address, the values were correct when I printed them out, but then when I save the address to the address field of the new contact, all of the fields of the address are blank except the pk...
To answer your direct question, you may want to use the save(force_insert=True) function and see if that fixes it. I would also check what you get if you call Contact.objects.all().count() and the same for Address, so you can ensure you are adding new records.
That said, I personally will recommend against what you are trying to do, which in my book, is a hack. Instead, just write the few extra lines of code and properly call the Adress.objects.create() and Contact.objects.create with the fields set from the other records. e.g.
old_address = self.object.designer.address
new_address = Address.objects.create(line1=old_adress.line1, line2=old_address.line2, etc)
Or even better, use an AddressManager:
class AddressManager(models.Manager):
def create_copy(self, obj):
address = self.create(line1=obj.line1, etc.)
return address
class ContactManager(models.Manager):
def create_copy(self, obj):
new_address = Address.objects.create_copy(obj.address)
contact = self.create(name=obj.name, address=new_address, etc.)
return contact
new_contact = Contact.objects.create_copy(old_contact)
Hope this helps.
I think you're not clear about how to define relationship. If Contact model has one to one relationship with Address model, then one object of Contact class can be related to one object of Address model. It'll be defined as:
class Contact(models.Model):
# field_one = ...
# field_two = ...
# and so on...
class Address(models.Model):
contact = OneToOneField(Contact)
# and more model fields...
This way you can associate one Contact object to one Address object. If you want to have more than one address for one contact then you should use ForeignKey.
For one Contact object having related to many Address instances, you can define relationship as:
class Contact(models.Model):
# field_one = ...
# field_two = ...
# and so on...
class Address(models.Model):
contact = ForeignKey(Contact)
# and more fields...
But here an Address object can be associated to a particular Contact object only.
You can read about Many To Many realtionship here.
And you don't have to initialize pk field as it is automatically updated/added.
I'm having trouble doing an aggregation query on a many-to-many related field.
Here are my models:
class SortedTagManager(models.Manager):
use_for_related_fields = True
def get_query_set(self):
orig_query_set = super(SortedTagManager, self).get_query_set()
# FIXME `used` is wrongly counted
return orig_query_set.distinct().annotate(
used=models.Count('users')).order_by('-used')
class Tag(models.Model):
content = models.CharField(max_length=32, unique=True)
creator = models.ForeignKey(User, related_name='tags_i_created')
users = models.ManyToManyField(User, through='TaggedNote',
related_name='tags_i_used')
objects_sorted_by_used = SortedTagManager()
class TaggedNote(models.Model):
"""Association table of both (Tag , Note) and (Tag, User)"""
note = models.ForeignKey(Note) # Note is what's tagged in my app
tag = models.ForeignKey(Tag)
tagged_by = models.ForeignKey(User)
class Meta:
unique_together = (('note', 'tag'),)
However, the value of the aggregated field used is only correct when the model is queried directly:
for t in Tag.objects.all(): print t.used # this works correctly
for t in user.tags_i_used.all(): print t.used #prints n^2 when it should give n
Would you please tell me what's wrong with it? Thanks in advance.
I have figured out what's wrong and how to fix it now :)
As stated in the Django doc:
Django interprets the first Manager defined in a class as the "default" Manager, and several parts of Django will use that Manager exclusively for that model.
In my case, I should make sure that SortedTagManager is the first Manager defined.
2.I should have count notes instead of users:
Count('notes', distinct=True)