GraphQL Design with Circular Dependency - python

In my structure I would like to introduce circular dependency like below to avoid submiting two separate queries to backend. Can someone advise how this can be done in Python.
Below is the sample code:
parent.py
import graphene
class Parent(graphene.ObjectType):
id = graphene.ID()
name = graphene.String()
child= graphene.Field(Child)
child.py
import graphene
class Child(graphene.ObjectType):
id = graphene.ID()
name = graphene.String()
parent = graphene.Field(Parent)
test.py
from parent import Parent
print("TEST")
Error
ImportError: cannot import name 'Parent' from partially initialized module 'parent' (most likely due to a circular import)
Update
The following also doesn't work (circular import error)
import graphene
class Child(graphene.ObjectType):
import app.parent as P
id = graphene.ID()
name = graphene.String()
parent = graphene.Field(P.Parent)
...
import graphene
class Parent(graphene.ObjectType):
import app.child as C
id = graphene.ID()
name = graphene.String()
child = graphene.Field(C.Child)
...
from app.parent import Parent
print("TEST")
AttributeError: partially initialized module 'app.parent' has no attribute 'Parent' (most likely due to a circular import)

TLDR - graphene.Field('<class-loc>.<class-name>').
In your case, graphene.Field('parent.Parent') and graphene.Field('child.Child') should do the job.
Ran into the exact same issue and thought there must be some way to define the schema just using string representations instead. While going through the code I found the import_string function which is used internally that helped me understand how that can be done -
https://github.com/graphql-python/graphene/blob/a53b782bf8ec5612d5cceb582fbde68eeba859aa/graphene/utils/module_loading.py#L5

Just use "lambda". In your case:
child = graphene.Field(lambda:C.Child)
instead of:
child = graphene.Field(C.Child)

Related

How to avoid import errors in django within same app

I have a django app where I want MyModel instances to be saved using an Enum choice, which I process in the save() method, such as:
# app/models.py
from django.db import models
from app.utils import translate_choice
from enum import Enum
class MyEnum(Enum):
CHOICE_A = 'Choice A'
CHOICE_B = 'Choice B'
class MyModel(models.Model):
...
choice = models.CharField(
max_length=10,
choices=[(tag.name, tag.value) for tag in MyEnum],
)
...
def save(self, *args, **kwargs):
self.choice = translate_choice(self.choice)
super().save(*args, **kwargs)
Whereas on the app/utils.py I have:
from app.models import MyEnum
def translate_choice(value):
...
return MyEnum.CHOICE_A # For example
I keep getting ImportError: cannot import name 'MyEnum' errors on the app/utils.py file when running the application. Is this due to a python circular import error, or am I missing something else? When moving the translate_choice() method to app/models.py it stops happening, but I would like to use this same method in other modules and it is kind of weird having a transforming feature within the models when used in another app.
Thank you in advance
It's probably due to the circular import, as you've guessed yourself. You can try putting the import statement not at the top of the file, but inside the function that uses the imported object:
def translate_choice(value):
from app.models import MyEnum
...
return MyEnum.CHOICE_A # For example
This is, admittedly, not the most elegant solution. See also the answers to this post, where you can find other approaches.

Unable to use objects from a module in sys.modules

Because of circular imports a want to load a module this way:
try:
from ..foo import serializers as foo_serializers
except ImportError:
import sys
foo_serializers = sys.modules['app.foo.serializers']
When I use the loaded module in a class definition like this, it says app.foo.serializers has no attribute FooSerializer although it definitely has:
class SomeSerializer(ModelSerializer):
foo_field = foo_serializers.FooSerializer()
But when I use foo_serializers.FooSerializer in a class function it works. What does it mean? Are modules in sys.modules fully loaded in the time when the class definition is loaded? What could be a problem here?

AttributeError: module has no attribute while avoiding cyclic reference error

I would like to create small app using M-VC pattern in python. I am using PyCharm and my folder structure looks like this.
To avoid problem with cyclic references I am using import not from ... import.
Program executes without error when my viewController.py looks like this:
import model
class ViewController:
def initialize(self, mod):
self.model = mod
Adding model.Model expression inside initialize method results with an error: AttributeError: module 'model' has no attribute 'Model' Why it works in model.py with viewController.ViewController? And what is wrong?
[new]viewController.py
import model
class ViewController:
def initialize(self, mod:model.Model):
self.model = mod
Back/__init_.py
import model
import viewController
mModel = model.Model()
mVC = viewController.ViewController()
mModel.initializeApp(mVC)
model.py
import viewController
class Model():
def initializeApp(self, viewContr: viewController.ViewController):
self.vc = viewContr
self.vc.initialize(self)

Does python support to import package

I am using Python 2.7, and have following code strucure
model
__init__.py
order.py
cart.py
That is, I define a package named model, and in this package, I define a module order, and I define a class in order.py
class MyOrder(object):
def __init__(self, name):
self.name = name
def getname(self):
return self.name
In the cart.py, the code is:
import model
x = model.order.MyOrder("Book")
print x.getname()
When I run it, it complains that AttributeError: 'module' object has no attribute 'order',
But the following is correct:
import model.order
x = model.order.MyOrder("Book")
print x.getname()
It looks that I can't import package (like import model) ?
If you want to have model automatically import order so it's available, you should do that in __init__.py. Simply put the following inside model/__init__.py:
from . import order
After that, you should be able to access model.order with just import model.

Metaclass conflict when separating classes into separate files

Edit
The problem was in importing. What I should have done was write: from SomeInterface import SomeInterface. Really I should write module name in lowercase someinterface.py as per Python styleguide (PEP 8).
I have a file model.py the defines all classes related to my DB as well as instantiates my Base.
# model.py
metadata = MetaData()
DeclarativeBase = declarative_base()
metadata = DeclarativeBase.metadata
class Bar(DeclarativeBase):
__tablename__ = 'Bar'
__table_args__ = {}
# column and relation definitions
The file model.py is autogenerated so I can't really touch it. What I did instead was create a file called modelaugmented.py where I add extra functionality to some of the model classes via inheritance.
# modelaugmented.py
from model import *
import SomeInterface
class BarAugmented(Bar, SomeInterface):
pass
# SomeInterface.py
class SomeInterface(object):
some_method(): pass
The problem I'm having is that for classes like BarAugmented, I'm getting the following error:
TypeError: Error when calling the metaclass bases
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
I only get this error when SomeInterface is in a separate file instead of being inside modelaugmented.py.
I understand that the metaclass for SomeInterface and Bar are different. The problem is that I can't figure out how to resolve this problem. I tried the solution suggested in Triple inheritance causes metaclass conflict... Sometimes which works in the example given, but not in my case. Not sure if SqlAlchmey has anything to do with it.
class MetaAB(type(DeclarativeBase), type(SomeInterface)):
pass
class BarAugmented(Bar, SomeInterface):
__metaclass__ = MetaAB
But then I get the error:
TypeError: Error when calling the metaclass
bases multiple bases have instance lay-out conflict
Using SQLAlchemy 0.8 and Python 2.7.
Ok, there must be something I'm missing, because I created a similar file layout to yours (I think) and it works in my machine. I appreciate you kept your question short and simple, but maybe is missing some little detail that alters... something? Dunno... (maybe SomeInterface has an abc.abstract metaclass?) If you update your question, please let me know trough a comment to this answer, and I'll try to update my answer.
Here it goes:
File stack29A.py (equivalent to your model.py):
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session
DSN = "mysql://root:foobar#localhost/so_tests?charset=utf8"
engine = create_engine(DSN)
Session = scoped_session(sessionmaker(bind=engine))
session = Session()
DeclarativeBase = declarative_base()
class Bar(DeclarativeBase):
__tablename__ = 'Bar'
_id = Column('_id', Integer, primary_key=True)
email = Column('email', String(50))
File stack29B.py (equivalent to your someinterface.py):
class SomeInterface(object):
def some_method(self):
print "hellou"
File stack29C.py (equivalent to your modelaugmented.py):
from stack29A import Bar
from stack29B import SomeInterface
class BarAugmented(Bar, SomeInterface):
pass
File stack29D.py (like a kind of main.py: table creator and sample):
from stack29C import BarAugmented
from stack29A import session, engine, DeclarativeBase
if __name__ == "__main__":
DeclarativeBase.metadata.create_all(engine)
b1 = BarAugmented()
b1.email = "foo#bar.baz"
b2 = BarAugmented()
b2.email = "baz#bar.foo"
session.add_all([b1, b2])
session.commit()
b3 = session.query(BarAugmented)\
.filter(BarAugmented.email == "foo#bar.baz")\
.first()
print "b3.email: %s" % b3.email
b3.some_method()
If I run the "main" file (stack29D.py) everything works as expected:
(venv_SO)borrajax#borrajax:~/Documents/Tests$ python ./stack29D.py
b3.email: foo#bar.baz
hellou

Categories

Resources