files impl
This commit is contained in:
parent
1048122def
commit
b680ca8bbd
@ -1 +1,2 @@
|
||||
name
|
||||
admin
|
|
@ -1,2 +1,2 @@
|
||||
email,password,role_names
|
||||
admin@blog.pi2.dev,__SENSITIVE__.ADMIN_PASSWORD,admin
|
||||
admin@app,__SENSITIVE__.ADMIN_PASSWORD,admin
|
|
@ -10,15 +10,21 @@ access:
|
||||
login_required: true
|
||||
roles_required:
|
||||
- admin
|
||||
- verb: get
|
||||
login_required: false
|
||||
- verb: list
|
||||
login_required: false
|
||||
- verb: table
|
||||
login_required: false
|
||||
- verb: search
|
||||
- verb: get|list|table|search|create|update|delete
|
||||
login_required: false
|
||||
show_in_nav: yes
|
||||
columns:
|
||||
- name: filename
|
||||
- name: body
|
||||
type: long_text
|
||||
- name: body|is_<bool>|<date>_dt
|
||||
type: boolean|integer|text|long_text|datetime|filename|
|
||||
default: "{}"
|
||||
- name: related
|
||||
type: relationship # type relationship
|
||||
multiple: true # could be many-to-many
|
||||
- name: choisy
|
||||
type: choice
|
||||
choices:
|
||||
INT_INFO: int_info # key:value
|
||||
display:
|
||||
primary: type
|
||||
secondary: text
|
||||
tertiary: alt_names
|
@ -1,3 +1,4 @@
|
||||
import os
|
||||
import importlib
|
||||
import json
|
||||
from collections import defaultdict
|
||||
@ -11,6 +12,7 @@ from sqlalchemy_filters import apply_filters
|
||||
|
||||
from oshipka.persistance import db, filter_m_n, update_m_ns
|
||||
from oshipka.util.strings import camel_case_to_snake_case
|
||||
from config import MEDIA_DIR
|
||||
|
||||
webapp_models = importlib.import_module("webapp.models")
|
||||
|
||||
@ -30,6 +32,14 @@ def default_get_form_func(vc):
|
||||
bool_values = request.form.getlist(k)
|
||||
bool_value = True if len(bool_values) == 2 else False
|
||||
vc.serialized_args[k[1:]] = bool_value
|
||||
for k, f in request.files.items():
|
||||
if f.filename != '':
|
||||
filedir = os.path.join(MEDIA_DIR, vc.model_view.model_name_pl)
|
||||
if not os.path.exists(filedir):
|
||||
os.makedirs(filedir)
|
||||
vc.serialized_args[k] = os.path.join(vc.model_view.model_name_pl, f.filename)
|
||||
filepath = os.path.join(filedir, f.filename)
|
||||
f.save(filepath)
|
||||
vc.serialized_args.update(dict(filter(lambda k: not k[0].startswith("_"), dict(request.form).items())))
|
||||
to_delete = []
|
||||
for key, value in vc.serialized_args.items():
|
||||
@ -92,7 +102,14 @@ def default_update_func(vc):
|
||||
del vc.serialized_args[key]
|
||||
vc.instance = vc.instances[0]
|
||||
for k, v in vc.serialized_args.items():
|
||||
setattr(vc.instance, k, v)
|
||||
if k.startswith("_file_"):
|
||||
key = k.split('_file_')[1]
|
||||
current_value = getattr(vc.instance, key)
|
||||
if current_value and current_value != v:
|
||||
os.remove(os.path.join(MEDIA_DIR, v))
|
||||
setattr(vc.instance, key, v)
|
||||
else:
|
||||
setattr(vc.instance, k, v)
|
||||
update_m_ns(vc.instance, m_ns)
|
||||
db.session.add(vc.instance)
|
||||
|
||||
@ -105,6 +122,13 @@ def default_create_func(vc):
|
||||
|
||||
def default_delete_func(vc):
|
||||
instance = vc.instances[0]
|
||||
for filecolumn in vc.model_view.model_file_columns:
|
||||
filename = getattr(instance, filecolumn)
|
||||
if filename:
|
||||
try:
|
||||
os.remove(os.path.join(MEDIA_DIR, filename))
|
||||
except Exception as e:
|
||||
flash("Error deleting file: {}".format(e), "error")
|
||||
db.session.delete(instance)
|
||||
|
||||
|
||||
@ -226,6 +250,7 @@ class ModelView(object):
|
||||
_model_name = model.name
|
||||
self.model_name = camel_case_to_snake_case(_model_name)
|
||||
self.model_name_pl = p.plural(self.model_name)
|
||||
self.model_file_columns = model._file_columns
|
||||
|
||||
MODEL_VIEWS[self.model_name] = self
|
||||
|
||||
|
@ -6,6 +6,8 @@ class [[ name ]](db.Model, ModelController[% for inherit in interits %], [[ inhe
|
||||
[%- include "_model_choice_header_py" %]
|
||||
[%- include "_model_searchable_header_py" %]
|
||||
|
||||
_file_columns = [ [%- for column in columns %][%- if column.is_file %]"[[ column.name ]]"[%- endif %] [%- endfor %] ]
|
||||
|
||||
[%- for column in columns %]
|
||||
[%- if column._type == 'relationship' %]
|
||||
[%- include "_relationship_py" %]
|
||||
|
@ -1,4 +1,4 @@
|
||||
<form action="{{ url_for('create_[[ name|camel_to_snake ]]') }}" method="post">
|
||||
<form action="{{ url_for('create_[[ name|camel_to_snake ]]') }}" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="_next" value="{{ _next or request.args.get('_next') or url_for('list_[[ name|camel_to_snake ]]') }}"/>
|
||||
<table>
|
||||
[%- for column in columns %]
|
||||
@ -44,6 +44,8 @@
|
||||
[%- elif column.type in ['long_text', ] %]
|
||||
<textarea id="input-[[ name|camel_to_snake ]]-[[ column.name ]]"
|
||||
name="[[ column.name ]]"></textarea>
|
||||
[%- elif column.type in ['file', 'audio', 'video', 'image', 'picture', 'img', ] %]
|
||||
<input type="file" name="_file_[[ column.name ]]" accept="[[ column.accept ]]">
|
||||
[%- else %]
|
||||
<input id="input-[[ name|camel_to_snake ]]-[[ column.name ]]"
|
||||
type="text" name="[[ column.name ]]" autocomplete="off"
|
||||
|
@ -1,6 +1,9 @@
|
||||
[%- for column in columns %]
|
||||
{% if "[[ column.name ]]" not in skip_list %}
|
||||
[%- if column.type in ['video'] %]
|
||||
<li id="display-[[ name|camel_to_snake ]]-[[ column.name ]]"><strong>{{ _("[[ column.name ]]") }}</strong>:
|
||||
[%- if column.type in ['picture', 'image', 'img'] %]
|
||||
<img src="{{ url_for('oshipka_bp.get_media', filepath=instance.[[ column.name ]]) }}" id="display-[[ name|camel_to_snake ]]-[[ column.name ]]" />
|
||||
[%- elif column.type in ['video'] %]
|
||||
<video src="{{ url_for('oshipka_bp.get_media', filepath=instance.[[ column.name ]]) }}" controls class="video-inline" id="display-[[ name|camel_to_snake ]]-[[ column.name ]]">
|
||||
<source src="{{ url_for('oshipka_bp.get_media', filepath=instance.[[ column.name ]]) }}" type="video/mp4">
|
||||
<source src="{{ url_for('oshipka_bp.get_media', filepath=instance.[[ column.name ]]) }}" type="video/webm">
|
||||
@ -9,14 +12,15 @@
|
||||
<audio src="{{ url_for('oshipka_bp.get_media', filepath=instance.[[ column.name ]]) }}" controls id="display-[[ name|camel_to_snake ]]-[[ column.name ]]"></audio>
|
||||
[%- elif column.type in ['relationship'] %]
|
||||
[%- if column.multiple %]
|
||||
<li id="display-[[ name|camel_to_snake ]]-[[ column.name ]]"><strong>{{ _("[[ column.name|pluralize ]]") }}</strong>: {{ instance.[[ column.name|pluralize ]] }}</li>
|
||||
{{ instance.[[ column.name|pluralize ]] }}
|
||||
[%- else %]
|
||||
<li id="display-[[ name|camel_to_snake ]]-[[ column.name ]]"><strong>{{_("[[ column.name ]]") }}</strong>: {{ instance.[[ column.name ]] }}</li>
|
||||
{{ instance.[[ column.name ]] }}
|
||||
[%- endif %]
|
||||
[%- elif column.type in ['bool', 'boolean', ] %]
|
||||
<li id="display-[[ name|camel_to_snake ]]-[[ column.name ]]"><strong>{{ _("[[ column.name ]]") }}</strong>: {{ instance.[[ column.name ]]|bool }}</li>
|
||||
{{ instance.[[ column.name ]]|bool }}
|
||||
[%- else %]
|
||||
<li id="display-[[ name|camel_to_snake ]]-[[ column.name ]]"><strong>{{ _("[[ column.name ]]") }}</strong>: {{ instance.[[ column.name ]] }}</li>
|
||||
{{ instance.[[ column.name ]] }}
|
||||
[%- endif %]
|
||||
</li>
|
||||
{% endif %}
|
||||
[%- endfor %]
|
@ -1,4 +1,4 @@
|
||||
<form action="{{ url_for('update_[[ name|camel_to_snake ]]', uuid=instance.id) }}" method="post">
|
||||
<form action="{{ url_for('update_[[ name|camel_to_snake ]]', uuid=instance.id) }}" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="_next" value="{{ _next or request.args.get('_next') or url_for('get_[[ name|camel_to_snake ]]', uuid=instance.id) }}"/>
|
||||
<table>
|
||||
[%- for column in columns %]
|
||||
@ -43,6 +43,9 @@
|
||||
[%- elif column.type in ['long_text', ] %]
|
||||
<textarea id="input-[[ name|camel_to_snake ]]-[[ column.name ]]"
|
||||
name="[[ column.name ]]">{{ instance.[[ column.name ]] }}</textarea>
|
||||
[%- elif column.type in ['file', 'audio', 'video', 'image', 'picture', 'img', ] %]
|
||||
<p>{{ instance.[[ column.name ]] }}</p>
|
||||
<input type="file" name="_file_[[ column.name ]]" accept="[[ column.accept ]]">
|
||||
[%- else %]
|
||||
<input id="input-[[ name|camel_to_snake ]]-[[ column.name ]]"
|
||||
value="{{ instance.[[ column.name ]] }}"
|
||||
|
19
vm_gen/templates/html/navigation.html
Normal file
19
vm_gen/templates/html/navigation.html
Normal file
@ -0,0 +1,19 @@
|
||||
<a href="{{ url_for('home') }}">{{ _("Home") }}</a> |
|
||||
[%- for nav in navigation_items %]
|
||||
[%- if nav._verbs.list.is_login_required %]
|
||||
{% if current_user.is_authenticated %}
|
||||
[%- endif %]
|
||||
<a href="{{ url_for('list_[[ nav.name|camel_to_snake ]]') }}">{{ _("[[ nav.name ]]") }}</a>[%- if not loop.last %]|[%- endif %]
|
||||
[%- if nav._verbs.list.is_login_required %]
|
||||
{% endif %}
|
||||
[%- endif %]
|
||||
[%- endfor %]
|
||||
|
||||
<div class="pull-right">
|
||||
{% if current_user.is_authenticated %}
|
||||
<a href="#">{{ current_user.email }}</a> |
|
||||
<a href="{{ url_for('security.logout') }}">{{ _("Logout") }}</a> |
|
||||
{% else %}
|
||||
<a href="{{ url_for('security.login') }}">{{ _("Login") }}</a> |
|
||||
{% endif %}
|
||||
</div>
|
@ -37,6 +37,24 @@ def process_secondary(view_model, column_name):
|
||||
}
|
||||
|
||||
|
||||
def _process_file(column):
|
||||
column_type = column.get('type', '')
|
||||
column_accept = column.get('accept', '')
|
||||
if column_accept:
|
||||
accept = column_accept
|
||||
elif column_type in ['audio']:
|
||||
accept = 'audio/*'
|
||||
elif column_type in ['video']:
|
||||
accept = 'video/*'
|
||||
elif column_type in ['video']:
|
||||
accept = 'video/*'
|
||||
else:
|
||||
accept = '*'
|
||||
column["accept"] = accept
|
||||
column["is_file"] = True
|
||||
return column
|
||||
|
||||
|
||||
VERBS = {
|
||||
'get': {
|
||||
'per_item': 'True',
|
||||
@ -97,6 +115,9 @@ def enrich_view_model(view_model):
|
||||
_choices = _process_choice(column)
|
||||
_column_type = 'ChoiceType({})'.format(_choices.get('name'))
|
||||
view_model['_choice_types'].append(_choices)
|
||||
elif column_type in ['file', 'audio', 'video', 'image', 'img', 'picture', ]:
|
||||
column = _process_file(column)
|
||||
_column_type = 'db.UnicodeText'.format()
|
||||
else:
|
||||
_column_type = 'db.UnicodeText'
|
||||
column.update({'_type': _column_type})
|
||||
@ -124,6 +145,18 @@ def enrich_view_model(view_model):
|
||||
return view_model
|
||||
|
||||
|
||||
def process_navigation(view_models):
|
||||
navigation_items = []
|
||||
for view_model in view_models:
|
||||
if view_model.get('show_in_nav'):
|
||||
navigation_items.append(view_model)
|
||||
template = env.get_template(os.path.join('html', 'navigation.html'))
|
||||
rv = template.render(**{"navigation_items": navigation_items})
|
||||
filepath = os.path.join(HTML_TEMPLATES_PATH, "navigation.html")
|
||||
with open(filepath, 'w+') as f:
|
||||
f.write(rv)
|
||||
|
||||
|
||||
def process_model(view_model):
|
||||
template = env.get_template('model_py')
|
||||
model = autopep8.fix_code(template.render(**view_model), options=pep_options)
|
||||
@ -171,13 +204,14 @@ def process_html_templates(view_model):
|
||||
|
||||
def main():
|
||||
all_model_imports = ['from oshipka.persistance import db']
|
||||
all_route_imports = []
|
||||
all_route_imports, all_view_models = [], []
|
||||
view_model_names = order_from_process_order('yaml', VIEW_MODELS_PATH)
|
||||
for view_model_name in view_model_names:
|
||||
with open(os.path.join(VIEW_MODELS_PATH, "{}.yaml".format(view_model_name)), 'r') as stream:
|
||||
try:
|
||||
view_models = yaml.safe_load_all(stream)
|
||||
for view_model in view_models:
|
||||
all_view_models.append(view_model)
|
||||
view_model = enrich_view_model(view_model)
|
||||
process_model(view_model)
|
||||
process_routes(view_model)
|
||||
@ -191,6 +225,7 @@ def main():
|
||||
))
|
||||
except yaml.YAMLError as e:
|
||||
breakpoint()
|
||||
process_navigation(all_view_models)
|
||||
all_model_imports = autopep8.fix_code('\n'.join(all_model_imports), options=pep_options)
|
||||
all_route_imports = autopep8.fix_code('\n'.join(all_route_imports), options=pep_options)
|
||||
with open(os.path.join(MODELS_PATH, "__init__.py"), 'w+') as f:
|
||||
|
Loading…
Reference in New Issue
Block a user