I had been dynamic create a python meta class
I want to dump it to a python file
cls = type("ClsName", (object, ), attr)
code = xxxGetSource(cls)
writeToFile(code, "moduleName/ClsName.py")
I need this, because when django makemigrations, it need to found the metaclass for model, but my model metaclass was dynamic generate
class XXXModel(GenerateCls({ .... }),
models.Model):
pass
Although Django does have tools that enable doing this, it is not actually needed.
What you need to have is that upon importing models.py, your dynamically created classes must be available on the module namespace.
So, all you have to do is to call upon whatever code does create the classes in code at the models.py module level, and ensure they are visible there - it can be as simple as:
from django.db import models
def create_classes():
Meta = type("Meta", (), {"app_label": "test2"})
DynamicUser = type("DynamicUser", (models.Model,), {"name": models.TextField(255), "__module__": __file__, "Meta": Meta})
return DynamicUser
DynamicUser = create_classes()
if you don't know at coding time the name and quantity of classes, then you can update the dicionary returned by globals() with the classes and their names.
Let's suppose the create_classes function would return a list contaning classes - one could do this in models.py
def materialize_for_migration():
classes = create_classes
globals().update({cls.__name__: cls for cls in classes})
materialize_for_migration()
Related
I have a function make_fields_permissions that I need to use it inside the model calss in order to parse the fields and to make permissions for each field like [('can_view_first_name_field','can view first name'),...]
goal I need to call and override Person class and inside it self
I tried
def __init__(self,*args, **kwargs):
self.Meta.permissions = make_fields_permissions(self.model)
super().__init__(*args, **kwargs)
My code look like this
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
def __init__(self, *args, **kwargs):
# kwargs['user']['permissions'] = make_fields_permissions(Profile)
# x = self['age']
super().__init__(*args, **kwargs)
class Meta:
permissions = make_fields_permissions(Person) #< I can't use the same model inside meta
Your goal is as follows:
Goal X (Real goal): Create permissions dynamically according to the model fields
Goal Y (Perceived goal that will achieve X): Call the model class while creating it.
Note: See What is the XY problem?
Let us first discuss Goal Y and why it is too complex, and somewhat unfeasable. When one wants to customize how the creation of a class occurs one would use metaclasses, and at first sight this would appear as a perfect solution for your needs (in fact if you do create one properly it would be). But the problem here is that Model already has a metaclass being ModelBase and it is already doing lots of stuff and is a little complicated. If we would want a custom metaclass we would need to inherit from it and very carefully work around its implementation to do what we want. Furthermore making it would not be the end of the story, because then we would need to maintain it since it would be easily breakable by updates to Django. Hence Goal Y is not feasible.
Moving on to the actual Goal X to do that one can Programmatically create permissions [Django docs]. A good place to do this would be in the app configs ready method. For all apps created using startapp there is an apps.py file which has an appconfig inheriting from AppConfig, when the models are loaded its ready method is called. Hence this method is used to do various tasks like attaching signals, various setup like tasks, etc. Modify the appconfig of your app to create permissions programmatically like so:
from django.apps import AppConfig
class YourAppConfig(AppConfig):
default_auto_field = 'django.db.models.AutoField' # Don't modify, keep it as it is in your code
name = 'your_app' # Don't modify, keep it as it is in your code
def ready(self):
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from path.to import make_fields_permissions
from .models import Person
# import models here, not outside as models may not be loaded yet
content_type = ContentType.objects.get_for_model(Person)
for codename, name in make_fields_permissions(Person):
Permission.objects.get_or_create(
codename=codename, # 'can_view_first_name_field'
name=name, # 'can view first name'
content_type=content_type,
)
I'm building a factory with factory_boy that generates a django model. I would like to see what arguments the user inputs inline. My factory itself looks like this
class SomeFactory(factory.django.DjangoModelFactory):
name = factory.Sequence(lambda n: 'Instance #{}'.format(n))
some_other_thing = factory.SubFactory(SomeOtherFactory)
class Meta:
model = SomeModel
Now the user could say s = SomeFactory() and it would work fine, but I want to detect if the user input their own argument. For instance, to tell if the user passed in their own name, as in s = SomeFactory(name='Matt')
What I've tried so far is
Writing my own __init__ function in the SomeFactory class
This gets mysteriously overwritten and is neither called when I call s = SomeFactory(), nor when I call s.__init__()
Same goes for overwriting the __new__ method
Overwriting the poorly named _adjust_kwargs
This gives me all fields as kwargs, not just the ones the user defined. For instance, calling s = SomeFactory(name='Matt'), I would get a kwargs dict with keys for name and some_other_thing, which makes it impossible to tell input their own argument or not
Overwriting _create
Still encounter the same problem with overwriting _adjust_kwargs, in that kwargs doesn't contain the original kwargs, but rather all of the arguments
I think a lot of the functionality I'm after is black-boxed inside of factory_boy's StepBuilder (I suspect it's in the instantiate method) but I have no idea how to modify it to do what I want.
Does anyone have any thoughts on how to figure out which kwargs were set originally in the call to s = SomeFactory()? I.e. determine that if I said s = SomeFactory(name='Matt'), that the user manually set the name?
Thanks!
Update: I'm running django version 1.11.2, factory_boy version 2.8.1, and python version 3.5.2
You can override the create method to only get the user kwargs.
A full example would be something like this:
from django.contrib.auth.models import User
import factory
class UserFactory(factory.DjangoModelFactory):
username = factory.Sequence(
lambda n: 'test'
)
email = factory.Sequence(lambda n: 'user{0}#example.com'.format(n))
class Meta:
model = User
#classmethod
def create(cls, **kwargs):
# here you'll only have the kwargs that were entered manually
print(str(kwargs))
return super(UserFactory, cls).create(**kwargs)
So when I call it:
In [2]: UserFactory(username='foobar')
{'username': 'foobar'}
Out[2]: <User: foobar>
If you want to catch kwargs for other build strategies than create, you would also need to do this for the stub and build method.
So I'm trying to load a model dynamically as such :
urls.py
url(r'^list_view/([\w-]+)$', Generic_list.as_view()),
Here's my Generic_list class in views.py :
class Generic_list(ListView):
import importlib
module_name = 'models'
class_name = ?
module = __import__(module_name, globals(), locals(), class_name)
model = getattr(module, class_name)
template_name = 'django_test/generic_list.html'
About security, later I'll only allow classes from a white list.
So, what can I put in place of the question mark in order to get the class name from the url?
If you want to have your model loaded dynamically, you could do something like this:
class Generic_list(ListView):
template_name = 'django_test/generic_list.html'
#property
def model(self):
return # code that returns the actual model
More information about property. Basically, it will understand this method as an attribute of the class. Instead of having to define this attribute at the class level (meaning that the code will be evaluated when the file is imported by django), you make it look like it is an attribute, but it's acting like a method, allowing you to add business logic.
You could try to use property decorator and get_model method:
from django.views.generic import ListView
from django.apps import apps
class GenericList(ListView):
#property
def model(self):
# Obtain model name here and pass it to `get_model` below.
return apps.get_model(app_label='app_label', model_name='model_name')
Also, consider reading PEP 0008 for naming conventions.
Can I add a custom restriction for the folder contents in Plone 4.1. Eg. Restrict the folder to contain only files with extensions like *.doc, *.pdf
I am aware of the general restrictions like file/ folder/ page / image which is available in Plone
Not without additional development; you'd have to extend the File type with a validator to restrict the mime types allowed.
Without going into the full detail (try for yourself and ask more questions here on SO if you get stuck), here are the various moving parts I'd implement if I were faced with this problem:
Create a new IValidator class to check for allowed content types:
from zope.interface import implements
from Products.validation.interfaces.IValidator import IValidator
class LocalContentTypesValidator(object):
implements(IValidator)
def __init__(self, name, title='', description='')
self.name = name
self.title = title or name
self.description = description
def __call__(value, *args, **kwargs):
instance = kwargs.get('instance', None)
field = kwargs.get('field', None)
# Get your list of content types from the aq_parent of the instance
# Verify that the value (*usually* a python file object)
# I suspect you have to do some content type sniffing here
# Return True if the content type is allowed, or an error message
Register an instance of your validotor with the register:
from Products.validation.config import validation
validation.register(LocalContentTypesValidator('checkLocalContentTypes'))
Create a new subclass of the ATContentTypes ATFile class, with a copy of the baseclass schema, to add the validator to it's validation chain:
from Products.ATContentTypes.content.file import ATFile, ATFileSchema
Schema = ATFileSchema.schema.copy()
Schema['file'].validators = schema['file'].validators + (
'checkLocalContentTypes',)
class ContentTypeRestrictedFile(ATFile):
schema = Schema
# Everything else you need for a custom Archetype
or just alter the ATFile schema itself if you want this to apply to all File objects in your deployment:
from Products.ATContentTypes.content.file import ATFileSchema
ATFileSchema['file'].validators = ATFileSchema['file'].validators + (
'checkLocalContentTypes',)
Add a field to Folders or a custom sub-class to store a list of locally allowed content types. I'd probably use archetypes.schemaextender for this. There is plenty of documentation on this these days, WebLion has a nice tutorial for example.
You'd have to make a policy decision on how you let people restrict mime-types here of course; wildcarding, free-form text, a vocabulary, etc.
Given the following models:
class Module(models.Model):
pass
class Content(models.Model):
module = models.ForeignKey(Module, related_name='contents')
class Blog(Module):
pass
class Post(Content):
pass
I would like to be able to get all the "post" objects owned by blog doing something like:
b = Blog.objects.get(pk=1)
b.posts.all()
However, I haven't figured out a good way of doing this. I can't use b.contents.all() as I need Post instances and not Content instances. I won't ever have a root content object, every content object is going to be subclassed, but I can't use abstract classes as I want a central table with all my content in it and then there will be content_blog etc tables for all the unique inherited pieces of content.
I also tried doing this
class Content(models.Model):
module = models.ForeignKey(Module, related_name='%(class)')
but that failed miserably as far as I could tell.
The simplest way might add a method to Blog model to return a Post queryset, like this:
class Blog(Module):
def _get_posts(self):
return Post.objects.filter(module=self)
posts = property(_get_posts)
The problem is you have to add method for every sub-model. The related_name seems only works for abstract base class.
This solution comes to my mind:
# ...
class Blog(Module):
#property
def posts(self):
return self.contents
class Post(Content):
pass
This way, doing blog.posts is the same as doing blog.contents:
>>> blog = Blog.objects.get(pk=1)
>>> blog.posts.all()
# [ ... ]