Resolve circular import issue - python

I am creating an app using Flask with MongoEngine to connect to MongoDB.
My folder structure looks like this:
app/
__init__.py
mod_users/
__init__.py
constants.py
forms.py
models.py
views.py
mod_games/
__init__.py
constants.py
forms.py
models.py
views.py
Let's say my User and Game models are like the following:
mod_users/models.py
class User(db.Document):
email = db.EmailField()
username = db.StringField()
password = db.StringField()
mod_games/models.py
from app.mod_users.models import User
class Game(db.Document):
title = db.StringField()
creator = db.ReferenceField(User, reverse_delete_rule=db.CASCADE)
likes_count = db.IntField()
Now, my problem is that I would like the User to have a list of the game he likes. But I cannot use a reference field because I would have to import Game which would create a circular import.
This won't work:
from app.mod_games.models import Game
class User(db.Document):
email = db.EmailField()
username = db.StringField()
password = db.StringField()
liked_games = db.ListField(
db.ReferenceField(Game, reverse_delete_rule=db.PULL)
)
I thought about storing in every game a list of users who liked it, and then adding a static method in Game that would retrieve
a list of liked Game for a given user, but that doesn't seem to be a clean and efficient way to resolve this.

Although you have an answer - MongoEngine does cater for this as you can pass the string name of the class to a reference field eg:
class User(db.Document):
email = db.EmailField()
username = db.StringField()
password = db.StringField()
liked_games = db.ListField(
db.ReferenceField('Game', reverse_delete_rule=db.PULL)
)

M:N relationships to be modelled via Association class
Game and User have a relationship, where one Game can be liked by any number of Users and one User can like any number of Games.
This is typical M:N relationship, and this is to be modelled by an association class (take it as class modelling rule).
The class should have:
reference to User
reference to Game
any additional properties of this particular relationship, e.g. how many stars the use gave to this game.
The tuple User - Game must be unique.
When defining this type of class, you import from User and from Game module.
User and Game shall not import this association class (otherwise you would enter into circular references problem again)

As it was mentioned before you can pass a string name of a model to a reference field. That is the first thing you should do to avoid circular imports:
class User(db.Document):
company = db.ReferenceField('Company')
But when you have a method that uses some model, let's say, to aggregate some data, this way above doesn't helps - you still may get an circular imports issue. To resolve this try get_document function. See example below.
from mongoengine.base.common import get_document as get_model
class User(db.Document):
# fields definition omitted
def get_games(self):
Game = get_model('Game')
games = Game.objects.filter(user=self.pk)
# some code here

Related

Autofill Models in Django

What I'm trying to implement is an invite system while I develop my website, but I'm new to how Django works.
For now, I want to be able to create a random string in the admin panel, have those added to the database, and then be required for a user to register. Eventually I want to create a user group system on the front end website where I can generate the strings there versus the admin panel, and then be able to send them out that way, but I'll get to that later.
I have the Model showing successfully in the admin panel, but I don't know how to make a text field that's automatically filled out anytime I create a new one and have the string be random each time.
class randomString(models.Model):
name = models.CharField(max_length=200)
random = models.ManyToManyField(get_random_string(length=6))
This is my current code which is throwing out an error, but I assumed that it would, I did it this way just to check and see if it was this simple. I have found out that it's not.
You can simply use an UUID:
from uuid import uuid4
class randomString(models.Model):
name = models.CharField(max_length=200)
random = models.UUIDField(unique=True, default=uuid4)
If the UUID is too long, you can generate a shorter string:
def generate_random():
from django.utils.crypto import get_random_string
return get_random_string(length=11)
class randomString(models.Model):
name = models.CharField(max_length=200)
random = models.CharField(default=generate_random, max_length=11, unique=True)

Django permissions via related objects permissions

I am relatively new to Django and I'm looking for some guidance in how to setup permissions in a certain way. Basically I have an app that consists of a couple of models similar to this:
class Project(models.Model):
name = models.CharField(max_length=100)
users = models.ManyToManyField(CustomUser, related_name="projects")
class Task(models.Model):
name = models.CharField(max_length=100)
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="tasks")
class Asset(models.Model):
name = models.CharField(max_length=100)
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="assets")
My idea is that if a user is "assigned" to a project (via M2M field), that user will have access to all assets and tasks that are related to that Project. I have looked into django-guardian for per-object permissions and I think that could be the way to go, but to me it seems like I then would have to setup those permissions on each model..?
It feels like this should be a pretty common way of setting up permissions for any project-based app but I have a hard time finding similar examples and starting to wonder if I'm overthinking this or looking in the wrong direction?
Thank you,
Jonas
You can use django-rules to take advantage of object-level permissions without a database; with it, you can add permissions in many levels - models, views, templates, admin or DRF.
So, you'd need to create a predicate like
#rules.predicate
def is_project_manager(user, project):
return project.users == user
which will return True if the project's manager is the given user, False otherwise.
Then, to add it in a model, you'd do something like
import rules
from rules.contrib.models import RulesModel
class Project(RulesModel):
class Meta:
rules_permissions = {
"add": rules.is_project_manager,
"read": rules.is_authenticated,
}
There's ofc other considerations to attend to but I think that gives an overview of how it works.

is there a way to set peewee fields via a mixin?

I'm trying to set up a reusable set of data models which I can include in multiple apps, something like this (I'm using users as an example here, but the actual one is a peewee backend for the Authlib library):
# mixins.py
class UserMixin(peewee.Model):
username = peewee.CharField()
password = peewee.CharField()
def set_password(self):
# do stuff
...
Once that mixin's created, I should be able to import it like this, defining only the additional fields (the defaults will already be there from the mixin)
# models.py
db = peewee.SqliteDatabase(config.get('DATABASE_FILE'))
class BaseModel(peewee.model):
class Meta:
database = db
class User(BaseModel, UserMixin):
email = peewee.CharField()
...
I've seen people do this with SQLAlchemy, but when I use this strategy with peewee it doesn't seem to save the fields properly:
if UserMixin inherits from peewee.Model, it says "unable to resolve import hierarchy" (probably since we're importing from peewee.Model multiple times)
if UserMixin is just an object, then peewee doesn't seem to handle its fields properly: they all end up as unbound instances and don't get saved in the database.
My question: is there an "official way" to create reusable model mixins with fields in peewee?
I've seen other projects (such as flask-login) use mixins, but those are generally additional functions like set_password in this example, and not ones that define the fields themselves.
I have a few potential alternate solutions, like
Define the models themselves, rather than mixins, in the shared file, and override their .Meta.database separately for each models.py entry
Define only the other functions in the mixin; let the fields be defined separately each time in models.py
Use the shared code as a file to copy-paste from rather than importing directly.
But there's probably some cleaner way of doing this?
Here's a simple example:
from peewee import *
db = SqliteDatabase(':memory:')
class Base(Model):
class Meta:
database = db
class UserModelMixin(Model):
username = TextField()
class User(UserModelMixin, Base):
pass
print(User._meta.fields)
#{'id': <AutoField: User.id>, 'username': <TextField: User.username>}
I think the problem was the ordering of your mixins.

Foreign key use with user model upload

These are my models and one user can upload multiple videos but one video belongs only to one user. How do I use the foreign key concept over here? When I add a user, does this automatically add a username in the Video model? If not, how do I do that? I'm very new to django over here
class User(models.Model):
first_name=models.CharField(max_length=20)
last_name=models.CharField(max_length=20)
username=models.CharField(max_length=25, primary_key=True)
password=models.CharField(max_length=15)
email_id=models.CharField(max_length=30, default='NULL')
profile_pic=models.ImageField(upload_to='profilepics/%Y/%m/%d/',default='')
def __str__(self):
return self.username
class Video(models.Model):
username=models.ForeignKey(User,on_delete=models.CASCADE,default="")
video=models.FileField(upload_to='videos/%Y/%m/%d/',default='')
videotitle=models.CharField(max_length=100)
likes=models.PositiveIntegerField(default=0)
dislikes=models.PositiveIntegerField(default=0)
def __str__(self):
return self.video
Try the following
from django.db import models
from django.conf import settings
class Video(models.Model):
...
username = models.ForeignKey(settings.AUTH_USER_MODEL)
Instead of referring to User directly, you should reference the user model using django.contrib.auth.get_user_model(). This method will return the currently active User model – the custom User model if one is specified, or User otherwise.
More info can be found here: https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#referencing-the-user-model
Not, it won't do it automatically -- and how would it do that. You need to pass user. Also you definitely don't want to add a username, but a reference to a User object. This is misleading in the code as you have a "username" in the Video class, which acutally is not just a name (string), but a ForeignKey -- a reference to an object User when adding the new video. So what you need to do when adding a video is something along the lines of:
def add_new_video(username, filename, title):
owner = User.objects.get(username=username)
newvid = Video(username=owner, video=filename, videotitle=title)
newvid.save()
assuming that likes and dislikes will be added later on...

Google App Engine - Multiple child/parent

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".

Categories