As of 2020, while using neomodel 4.0.1
answer by All Іѕ Vаиітy doesn't produce the desired outcome. Therefore the answer from user3280193 is the most correct, but not without a small caveat, so let me elaborate.
Label Hacking (not recommended!)
First let's look, why label hacking is flawed:
class Unemployed(StructuredNode):
__label__ = 'Person:Unemployed'
name = StringProperty(unique_index=True)
Unemployed(name='Carol').save()
If you check, it cannot detect nodes later on properly, even if they are correctly saved in the database:
print(len(Unemployed.nodes)) # prints 0

One could think that if we had another class Person
, then we could retrieve it that way - unfortunately not. See for yourself:
class Unemployed(StructuredNode):
__label__ = 'Person:Unemployed'
name = StringProperty(unique_index=True)
class Person(StructuredNode):
name = StringProperty(unique_index=True)
Unemployed(name='Carol').save()
So far, so good, so let's try to get some nodes. The following outcome looks well.
print(len(Person.nodes)) # prints 1
However, the problem will arise when we try to access that node:
print(Person.nodes[0])
# Results in two exceptions
#
# Traceback (most recent call last):
# ...
# KeyError: frozenset({'Person', 'Unemployed'})
#
# During handling of the above exception, another exception occurred:
# ...
# neomodel.exceptions.ModelDefinitionMismatch: <exception str() failed>
I will not go into details why this happens, but simply put neomodel can't cope with label hacking as it was not designed for it. If anyone wants to understand this behaviour, I suggest looking into neomodel.core
part of the library.
Inheritance
Officially, neomodel promotes inheritance and mixins. Read more at:
https://neomodel.readthedocs.io/en/latest/extending.html#inheritance
https://neomodel.readthedocs.io/en/latest/extending.html#mixins
As mixins don't provide additional labels, I will focus on inheritance. Let's assume the following example, where we go 2 levels deep into inheritance.
class Person(StructuredNode):
name = StringProperty(unique_index=True)
class Student(Person):
pass
class Employed(Person):
pass
class EmployedStudent(Student, Employed):
pass
Person(name='Bob').save()
Student(name='Will').save()
Employed(name='John').save()
EmployedStudent(name='Kim').save()
Results:
print(len(Person.nodes)) # 4
print(len(Student.nodes)) # 2
print(len(Employed.nodes)) # 2
print(len(EmployedStudent.nodes)) # 1

This has the correct behaviour but seemingly produces one artefact - the label EmployedStudent
. There is no simple hack to get rid of this additional label as it is crucial for automatic class resolution.
Conclusion: OGM has its cons, but I would anytime opt for the additional redundant label over writing cypher queries by myself for every class I construct.