Fixes in multiple things for modern systems
This commit is contained in:
parent
c31f61aa1b
commit
c9f77b191a
13 changed files with 829 additions and 81 deletions
|
|
@ -1,15 +1,38 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Parameciofm is a series of wrappers for bottle.py, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from paramecio.citoplasma.sessions import get_session
|
from paramecio.citoplasma.sessions import get_session
|
||||||
import json
|
import json
|
||||||
from bottle import request
|
from bottle import request
|
||||||
|
import gettext
|
||||||
|
import os
|
||||||
|
|
||||||
yes_session=False
|
yes_session=False
|
||||||
|
|
||||||
i18n_module={}
|
i18n_module={}
|
||||||
|
|
||||||
def load_lang(*args):
|
def load_lang(*args):
|
||||||
|
"""A function for load the lang module dinamically
|
||||||
|
"""
|
||||||
|
|
||||||
for module in args:
|
for module in args:
|
||||||
|
|
||||||
|
|
@ -26,9 +49,57 @@ def load_lang(*args):
|
||||||
|
|
||||||
# here load the language
|
# here load the language
|
||||||
|
|
||||||
|
class PGetText:
|
||||||
|
|
||||||
|
# Dict where all gettext domain are saved -> domain=name, example, admin, libraries, pastafari2, etc...
|
||||||
|
|
||||||
|
l={}
|
||||||
|
|
||||||
|
def __init__(self, module_file):
|
||||||
|
|
||||||
|
module_dir=os.path.dirname(os.path.realpath(module_file))
|
||||||
|
|
||||||
|
module_name=os.path.basename(module_dir)
|
||||||
|
|
||||||
|
if module_name not in PGetText.l:
|
||||||
|
|
||||||
|
PGetText.l[module_name]={}
|
||||||
|
|
||||||
|
for i in I18n.dict_i18n:
|
||||||
|
|
||||||
|
if i not in PGetText.l[module_name]:
|
||||||
|
|
||||||
|
PGetText.l[module_name][i]=gettext.translation(module_name, module_dir+'/languages/', languages=[i], fallback=True)
|
||||||
|
PGetText.l[module_name][i].install()
|
||||||
|
|
||||||
|
self.module=module_name
|
||||||
|
|
||||||
|
def gettext(self, text):
|
||||||
|
|
||||||
|
return PGetText.l[self.module][I18n.get_default_lang()].gettext(text)
|
||||||
|
|
||||||
|
def pgettext(module_file):
|
||||||
|
|
||||||
|
module=os.path.dirname(os.path.realpath(module_file))
|
||||||
|
|
||||||
|
base_name=os.path.dirname(os.path.realpath(module))
|
||||||
|
|
||||||
|
l=gettext.translation(os.path.basename(base_name), module+'/languages/', languages=I18n.get_default_lang(), fallback=True)
|
||||||
|
|
||||||
|
return l.gettext
|
||||||
|
|
||||||
class I18n:
|
class I18n:
|
||||||
|
|
||||||
|
"""Class for i18n tasks
|
||||||
|
|
||||||
|
Class for i18n tasks, how, strings for every language supported, for now are en-US and es-ES. You can add more languages adding
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
default_lang (str): The default string lang used when get someone
|
||||||
|
dict_i18n (list): The list with default languages. You can add more calling it static variable in settings/config.py
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
default_lang='en-US'
|
default_lang='en-US'
|
||||||
|
|
||||||
dict_i18n=['en-US', 'es-ES']
|
dict_i18n=['en-US', 'es-ES']
|
||||||
|
|
@ -42,6 +113,7 @@ class I18n:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_default_lang():
|
def get_default_lang():
|
||||||
|
"""Static method for get the default lang"""
|
||||||
|
|
||||||
lang=I18n.default_lang
|
lang=I18n.default_lang
|
||||||
|
|
||||||
|
|
@ -53,6 +125,15 @@ class I18n:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def lang(module, symbol, text_default, lang=None):
|
def lang(module, symbol, text_default, lang=None):
|
||||||
|
"""Static method for get a string from selected language
|
||||||
|
|
||||||
|
Static method used to get the string of the selected language. If there is no string in the selected language, it returns text_default.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
module (str): The module to which the translation string belongs
|
||||||
|
symbol (str): Simple symbol that is useful for identify the string
|
||||||
|
text_default (str): The text used by default when there are not translation in the selected language
|
||||||
|
"""
|
||||||
|
|
||||||
if not lang:
|
if not lang:
|
||||||
lang=I18n.get_default_lang()
|
lang=I18n.get_default_lang()
|
||||||
|
|
@ -67,7 +148,12 @@ class I18n:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def extract_value(value):
|
def extract_value(value):
|
||||||
|
"""Static method for get values from json lang array
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (json): Lang dict in json format
|
||||||
|
"""
|
||||||
|
|
||||||
value=json.loads(value)
|
value=json.loads(value)
|
||||||
|
|
||||||
lang=I18n.get_default_lang()
|
lang=I18n.get_default_lang()
|
||||||
|
|
@ -93,3 +179,4 @@ class I18n:
|
||||||
|
|
||||||
return json.dumps(arr_final)
|
return json.dumps(arr_final)
|
||||||
|
|
||||||
|
common_pgettext=PGetText(__file__)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ except:
|
||||||
key_encrypt=create_key_encrypt_256(30)
|
key_encrypt=create_key_encrypt_256(30)
|
||||||
session_opts={'session.data_dir': 'sessions', 'session.type': 'file', 'session.path': 'paramecio'}
|
session_opts={'session.data_dir': 'sessions', 'session.type': 'file', 'session.path': 'paramecio'}
|
||||||
|
|
||||||
from itsdangerous import JSONWebSignatureSerializer
|
#from itsdangerous import JSONWebSignatureSerializer
|
||||||
from bottle import request, response
|
from bottle import request, response
|
||||||
import os
|
import os
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ from settings import config
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
|
"""Module for create new modules for paramecio
|
||||||
|
"""
|
||||||
|
|
||||||
parser=argparse.ArgumentParser(description='A tool for create new modules for paramecio')
|
parser=argparse.ArgumentParser(description='A tool for create new modules for paramecio')
|
||||||
|
|
||||||
|
|
@ -99,11 +101,8 @@ def regenerate_modules_config():
|
||||||
print("-"*60)
|
print("-"*60)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
f=open('./settings/modules.py', 'w')
|
with open('./settings/modules.py', 'w') as f:
|
||||||
|
f.write("".join(modules))
|
||||||
f.write("".join(modules))
|
|
||||||
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
start()
|
start()
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,46 @@
|
||||||
|
"""
|
||||||
|
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
from paramecio.cromosoma.webmodel import PhangoField
|
from paramecio.cromosoma.webmodel import PhangoField
|
||||||
from paramecio.cromosoma import coreforms
|
from paramecio.cromosoma import coreforms
|
||||||
from paramecio.citoplasma.i18n import I18n
|
from paramecio.citoplasma.i18n import I18n
|
||||||
|
#from bs4 import BeautifulSoup
|
||||||
|
import bleach
|
||||||
|
|
||||||
class IntegerField(PhangoField):
|
class IntegerField(PhangoField):
|
||||||
|
|
||||||
"""Class that figure an integer sql type field.
|
"""Class that figure an integer sql type field.
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): The name of new field
|
|
||||||
size (int): The size of the new field in database. By default 11.
|
|
||||||
required (bool): Boolean for define if
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, size=11, required=False):
|
def __init__(self, name, size=11, required=False):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
name (str): The name of field
|
||||||
|
size (int): The size of the new field in database. By default 11.
|
||||||
|
required (bool): Boolean for define if field is required or not
|
||||||
|
"""
|
||||||
|
|
||||||
super(IntegerField, self).__init__(name, size, required)
|
super(IntegerField, self).__init__(name, size, required)
|
||||||
self.default_value=0
|
self.default_value=0
|
||||||
|
|
||||||
|
|
||||||
|
self.type_sql='int({})'.format(self.size)
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value):
|
||||||
|
|
||||||
|
|
@ -60,6 +85,11 @@ class BigIntegerField(IntegerField):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, size=11, required=False):
|
||||||
|
|
||||||
|
super().__init__(name, size, required)
|
||||||
|
self.type_sql='bigint({})'.format(self.size)
|
||||||
|
|
||||||
def get_type_sql(self):
|
def get_type_sql(self):
|
||||||
|
|
||||||
"""Method for return the sql code for this type
|
"""Method for return the sql code for this type
|
||||||
|
|
@ -85,6 +115,7 @@ class FloatField(PhangoField):
|
||||||
|
|
||||||
self.error_default="The value is zero"
|
self.error_default="The value is zero"
|
||||||
self.default_value=0
|
self.default_value=0
|
||||||
|
self.type_sql='float'.format(self.size)
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value):
|
||||||
|
|
||||||
|
|
@ -123,26 +154,55 @@ class FloatField(PhangoField):
|
||||||
return 'FLOAT NOT NULL DEFAULT "0"'
|
return 'FLOAT NOT NULL DEFAULT "0"'
|
||||||
|
|
||||||
class DecimalField(FloatField):
|
class DecimalField(FloatField):
|
||||||
|
"""PhangoField field for Decimals fields."""
|
||||||
|
|
||||||
|
def __init__(self, name, size=11, required=False):
|
||||||
|
|
||||||
|
super().__init__(name, size, required)
|
||||||
|
self.type_sql='decimal(20,2)'
|
||||||
|
|
||||||
def get_type_sql(self):
|
def get_type_sql(self):
|
||||||
|
|
||||||
return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"'
|
return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"'
|
||||||
|
|
||||||
class DoubleField(FloatField):
|
class DoubleField(FloatField):
|
||||||
|
"""PhangoField field for Double fields."""
|
||||||
|
|
||||||
|
def __init__(self, name, size=11, required=False):
|
||||||
|
|
||||||
|
super().__init__(name, size, required)
|
||||||
|
self.type_sql='double'
|
||||||
|
|
||||||
def get_type_sql(self):
|
def get_type_sql(self):
|
||||||
|
|
||||||
return 'DOUBLE NOT NULL DEFAULT "0"'
|
return 'DOUBLE NOT NULL DEFAULT "0"'
|
||||||
|
|
||||||
class CharField(PhangoField):
|
class CharField(PhangoField):
|
||||||
|
"""Simple alias for PhangoField"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class TextField(PhangoField):
|
class TextField(PhangoField):
|
||||||
|
"""Class used for text fields
|
||||||
|
|
||||||
|
Class used for text fields, use TEXT sql type for the this field.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, required=False):
|
def __init__(self, name, required=False):
|
||||||
|
"""Init TextField class different to standard PhangoField
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The name of new field
|
||||||
|
required (bool): Boolean for define if field is required or not
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
set_default (str): Set if the value es NOT NULL or not
|
||||||
|
"""
|
||||||
|
|
||||||
super().__init__(name, 11, required)
|
super().__init__(name, 11, required)
|
||||||
|
|
||||||
|
self.type_sql='text'
|
||||||
|
|
||||||
self.set_default='NOT NULL'
|
self.set_default='NOT NULL'
|
||||||
|
|
||||||
def get_type_sql(self):
|
def get_type_sql(self):
|
||||||
|
|
@ -153,19 +213,107 @@ class TextField(PhangoField):
|
||||||
|
|
||||||
return 'TEXT '+self.set_default
|
return 'TEXT '+self.set_default
|
||||||
|
|
||||||
class HTMLField(TextField):
|
class LongTextField(TextField):
|
||||||
|
"""Class used for long text fields (32 bits size, 4G)
|
||||||
|
|
||||||
|
Class used for text fields, use LONGTEXT sql type for the this field.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, required=False):
|
def __init__(self, name, required=False):
|
||||||
|
"""Init TextField class different to standard PhangoField
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The name of new field
|
||||||
|
required (bool): Boolean for define if field is required or not
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
set_default (str): Set if the value es NOT NULL or not
|
||||||
|
"""
|
||||||
|
|
||||||
super().__init__(name, required)
|
super().__init__(name, required)
|
||||||
|
self.type_sql='longtext'
|
||||||
|
|
||||||
|
def get_type_sql(self):
|
||||||
|
|
||||||
|
"""Method for return the sql code for this type
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return 'LONGTEXT '+self.set_default
|
||||||
|
|
||||||
|
class HTMLField(TextField):
|
||||||
|
"""Class used for html fields
|
||||||
|
|
||||||
|
Class used for html fields, use TEXT sql type for the this field because is children of TextField. In this method self.escape is used for convert " to "
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, required=False):
|
||||||
|
"""Init HTMLField class different to standard PhangoField
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (str): The name of new field
|
||||||
|
required (bool): Boolean for define if field is required or not
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
trusted_tags (list): A list with safe tags.
|
||||||
|
"""
|
||||||
|
|
||||||
|
super().__init__(name, required)
|
||||||
|
self.trusted_tags=[]
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value):
|
||||||
|
"""Check method for html values
|
||||||
|
|
||||||
return re.sub('<.*?script?>', '', value)
|
This check method use beautifulsoap for clean and format html code
|
||||||
|
"""
|
||||||
|
|
||||||
|
# leach.clean('<p>"trial"</p><script></script>', tags=('p'))
|
||||||
|
"""
|
||||||
|
soup=BeautifulSoup(value, features='html.parser')
|
||||||
|
|
||||||
|
for tag in soup.findAll(True):
|
||||||
|
|
||||||
|
if tag.name not in self.trusted_tags:
|
||||||
|
tag.hidden=True
|
||||||
|
|
||||||
|
value=soup.renderContents().decode('utf-8')
|
||||||
|
|
||||||
|
if self.escape:
|
||||||
|
|
||||||
|
return value.replace('"', '"')
|
||||||
|
else:
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
value=bleach.clean('<p>"trial"</p><script></script>', tags=self.trusted_tags)
|
||||||
|
|
||||||
|
if self.escape:
|
||||||
|
|
||||||
|
return value.replace('"', '"')
|
||||||
|
else:
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class ForeignKeyField(IntegerField):
|
class ForeignKeyField(IntegerField):
|
||||||
|
"""Subclass of IntegerField for create Foreign keys
|
||||||
|
|
||||||
|
A subclass of IntegerField used for create foreign keys in related tables.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, related_table, size=11, required=False, identifier_field='id', named_field="id", select_fields=[]):
|
def __init__(self, name, related_table, size=11, required=False, identifier_field='id', named_field="id", select_fields=[]):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
name (str): Name of field
|
||||||
|
related_table (WebModel): The table-model related with this foreign key
|
||||||
|
size (int): The size of the new field in database. By default 11.
|
||||||
|
required (bool): Boolean for define if field is required or not
|
||||||
|
identifier_field (str): The Id field name from related table
|
||||||
|
named_field (str): The field from related table used for identify the row seleted from related table
|
||||||
|
select_fields (list): A series of fields names from related
|
||||||
|
"""
|
||||||
|
|
||||||
super(ForeignKeyField, self).__init__(name, size, required)
|
super(ForeignKeyField, self).__init__(name, size, required)
|
||||||
|
|
||||||
|
|
@ -192,7 +340,7 @@ class ForeignKeyField(IntegerField):
|
||||||
value=super().check(value)
|
value=super().check(value)
|
||||||
|
|
||||||
if value=='0' or value==0:
|
if value=='0' or value==0:
|
||||||
value='NULL'
|
value=None
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
@ -206,8 +354,15 @@ class ForeignKeyField(IntegerField):
|
||||||
|
|
||||||
|
|
||||||
class BooleanField(IntegerField):
|
class BooleanField(IntegerField):
|
||||||
|
"""Field for boolean values
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, size=1):
|
def __init__(self, name, size=1):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
name (str): Name of field
|
||||||
|
size (int): The size of the new field in database. By default 11.
|
||||||
|
"""
|
||||||
|
|
||||||
required=False
|
required=False
|
||||||
|
|
||||||
|
|
@ -219,6 +374,8 @@ class BooleanField(IntegerField):
|
||||||
self.default_error="Need 0 or 1 value"
|
self.default_error="Need 0 or 1 value"
|
||||||
self.default_value=0
|
self.default_value=0
|
||||||
|
|
||||||
|
self.type_sql='tinyint(1)'
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value):
|
||||||
|
|
||||||
self.error=False
|
self.error=False
|
||||||
|
|
@ -231,6 +388,7 @@ class BooleanField(IntegerField):
|
||||||
if value<0 or value>1:
|
if value<0 or value>1:
|
||||||
self.txt_error=self.default_error
|
self.txt_error=self.default_error
|
||||||
self.error=True
|
self.error=True
|
||||||
|
value=0
|
||||||
|
|
||||||
except:
|
except:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,55 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from html import escape
|
from html import escape
|
||||||
|
|
||||||
#Forms para python3
|
#Forms para python3
|
||||||
|
|
||||||
class BaseForm:
|
class BaseForm:
|
||||||
|
"""The class used by all forms classes
|
||||||
|
|
||||||
|
BaseForm is the base class used for all form classes.
|
||||||
|
|
||||||
|
A form class is used for generate simple html forms, how input type, text type, hidden type, etc. PhangoField classes use this forms for generate automatically forms using GenerateAdminClass and others.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
name (str): The html name for this form
|
||||||
|
value (str): The default value of this html form.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
label (str): Label used in functions how show_form that generate a complete html form from a form class list
|
||||||
|
name (str): Name of the html form.
|
||||||
|
default_value (mixed): The default value of the form. Equal to value in typical html form.
|
||||||
|
css (str): Used for add css classes to the html form
|
||||||
|
type (str): Variable used for conventional html forms with html tag <input>
|
||||||
|
field (PhangoField): Field related with this form. Used in PhangoField.
|
||||||
|
required (boolean): If form is required or not, used in functions that generate forms.
|
||||||
|
name_field_id (str): The html id for the html form. Used for html things.
|
||||||
|
help (str): A string with help text, used in functions that generate forms.
|
||||||
|
"""
|
||||||
|
|
||||||
self.label=name
|
self.label=name
|
||||||
self.name=name
|
self.name=name
|
||||||
|
|
@ -17,26 +59,45 @@ class BaseForm:
|
||||||
self.field=None
|
self.field=None
|
||||||
self.required=False
|
self.required=False
|
||||||
self.txt_error=''
|
self.txt_error=''
|
||||||
|
self.error=False
|
||||||
self.name_field_id=self.name+'_form'
|
self.name_field_id=self.name+'_form'
|
||||||
self.help=''
|
self.help=''
|
||||||
|
self.placeholder=''
|
||||||
|
|
||||||
def form(self):
|
def form(self):
|
||||||
|
"""Method for returm the html code of the form
|
||||||
|
"""
|
||||||
|
|
||||||
return '<input type="'+self.type+'" class="'+self.css+'" name="'+self.name+'" id="'+self.name_field_id+'" value="'+self.setform(self.default_value)+'" />'
|
return '<input type="'+self.type+'" class="'+self.css+'" name="'+self.name+'" id="'+self.name_field_id+'" value="'+self.setform(self.default_value)+'" placeholder="'+self.placeholder+'" />'
|
||||||
|
|
||||||
def show_formatted(self, value):
|
def show_formatted(self, value):
|
||||||
|
"""Method for show the value of form formatted
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (mixed): The value of field form
|
||||||
|
"""
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
#Method for escape value for html input. DON'T CHANGE IF YOU DON'T KNOWN WHAT ARE YOU DOING
|
#Method for escape value for html input. DON'T CHANGE IF YOU DON'T KNOWN WHAT ARE YOU DOING
|
||||||
|
|
||||||
def setform(self, value):
|
def setform(self, value):
|
||||||
|
"""A method for set the value in the form for escape and other things
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (mixed): The value of field form for set
|
||||||
|
"""
|
||||||
|
|
||||||
value=str(value)
|
value=str(value)
|
||||||
|
|
||||||
return value.replace('"', '"').replace("'", ''')
|
return value.replace('"', '"').replace("'", ''')
|
||||||
|
|
||||||
def change_name(self, new_name):
|
def change_name(self, new_name):
|
||||||
|
"""A method for change the default form html name of the field form
|
||||||
|
|
||||||
|
Args:
|
||||||
|
new_name (str): The new name of the form. Always is finished with _form suffix
|
||||||
|
"""
|
||||||
|
|
||||||
self.name=new_name
|
self.name=new_name
|
||||||
|
|
||||||
|
|
@ -45,6 +106,8 @@ class BaseForm:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
class SimpleTextForm(BaseForm):
|
class SimpleTextForm(BaseForm):
|
||||||
|
"""Form for simple text
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
|
|
@ -56,6 +119,8 @@ class SimpleTextForm(BaseForm):
|
||||||
return super().form()+' '+self.after_text
|
return super().form()+' '+self.after_text
|
||||||
|
|
||||||
class TextForm(BaseForm):
|
class TextForm(BaseForm):
|
||||||
|
"""Form for simple text form
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
super(TextForm, self).__init__(name, value)
|
super(TextForm, self).__init__(name, value)
|
||||||
|
|
@ -65,6 +130,8 @@ class TextForm(BaseForm):
|
||||||
return '<textarea class="'+self.css+'" name="'+self.name+'" id="'+self.name+'_form">'+self.setform(self.default_value)+'</textarea>'
|
return '<textarea class="'+self.css+'" name="'+self.name+'" id="'+self.name+'_form">'+self.setform(self.default_value)+'</textarea>'
|
||||||
|
|
||||||
class PasswordForm(BaseForm):
|
class PasswordForm(BaseForm):
|
||||||
|
"""Form for password forms
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, value, show_password=False):
|
def __init__(self, name, value, show_password=False):
|
||||||
super(PasswordForm, self).__init__(name, value)
|
super(PasswordForm, self).__init__(name, value)
|
||||||
|
|
@ -79,6 +146,8 @@ class PasswordForm(BaseForm):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
class HiddenForm(BaseForm):
|
class HiddenForm(BaseForm):
|
||||||
|
"""Form for hidden forms
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
super(HiddenForm, self).__init__(name, value)
|
super(HiddenForm, self).__init__(name, value)
|
||||||
|
|
@ -86,8 +155,16 @@ class HiddenForm(BaseForm):
|
||||||
|
|
||||||
|
|
||||||
class SelectForm(BaseForm):
|
class SelectForm(BaseForm):
|
||||||
|
"""Form for select html form
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, value, elements=OrderedDict()):
|
def __init__(self, name, value, elements=OrderedDict()):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
name (str): The html name for this form
|
||||||
|
value (str): The default value of this html form
|
||||||
|
elements (OrderedDict): An ordered dict with the keys(the form value) and text label. Example, if you have a OrderedDict how {'0': 'Value selected'} in a html select form you have the next result: <select name="name"><option value="0">Value selected</option></selected>
|
||||||
|
"""
|
||||||
super(SelectForm, self).__init__(name, value)
|
super(SelectForm, self).__init__(name, value)
|
||||||
self.arr_select=elements
|
self.arr_select=elements
|
||||||
|
|
||||||
|
|
@ -107,8 +184,19 @@ class SelectForm(BaseForm):
|
||||||
return the_form
|
return the_form
|
||||||
|
|
||||||
class SelectModelForm(SelectForm):
|
class SelectModelForm(SelectForm):
|
||||||
|
"""Form for select html using a webmodel how base for get the data
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, value, model, field_name, field_value, field_parent=None):
|
def __init__(self, name, value, model, field_name, field_value, field_parent=None):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
name (str): The html name for this form
|
||||||
|
value (str): The default value of this html form.
|
||||||
|
model (WebModel): The webmodel used for get the data for arr_select
|
||||||
|
field_name (str): The field used for get the name of every option in select
|
||||||
|
field_value (str): The field used for get the value of every option in select
|
||||||
|
field_parent (int): If the model have parents or children, the value of this argument
|
||||||
|
"""
|
||||||
super(SelectModelForm, self).__init__(name, value)
|
super(SelectModelForm, self).__init__(name, value)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -129,12 +217,16 @@ class SelectModelForm(SelectForm):
|
||||||
|
|
||||||
|
|
||||||
def normal_form(self):
|
def normal_form(self):
|
||||||
|
"""Method for prepare the form hierated from SelectForm class, without parents
|
||||||
|
|
||||||
|
Method for prepare the form hierated from SelectForm class getting data from database using model attribute.
|
||||||
|
"""
|
||||||
|
|
||||||
self.arr_select['']=''
|
self.arr_select['']=''
|
||||||
|
|
||||||
with self.model.select([self.field_name, self.field_value], True) as cur:
|
with self.model.select([self.field_name, self.field_value], True) as cur:
|
||||||
for arr_value in cur:
|
for arr_value in cur:
|
||||||
|
#print(self.model.fields[self.field_name])
|
||||||
self.arr_select[arr_value[self.field_value]]=self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])
|
self.arr_select[arr_value[self.field_value]]=self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -147,6 +239,10 @@ class SelectModelForm(SelectForm):
|
||||||
return super().form()
|
return super().form()
|
||||||
|
|
||||||
def parent_form(self):
|
def parent_form(self):
|
||||||
|
"""Method for prepare the form hierated from SelectForm class, with parents
|
||||||
|
|
||||||
|
Method for prepare the form hierated from SelectForm class getting data from database using model attribute.
|
||||||
|
"""
|
||||||
|
|
||||||
self.arr_select['']=''
|
self.arr_select['']=''
|
||||||
|
|
||||||
|
|
@ -168,7 +264,8 @@ class SelectModelForm(SelectForm):
|
||||||
|
|
||||||
arr_son[arr_value[self.field_parent]]=[]
|
arr_son[arr_value[self.field_parent]]=[]
|
||||||
|
|
||||||
arr_son[arr_value[self.field_parent]].append([arr_value[self.field_value], arr_value[self.field_name]])
|
if arr_value[self.field_value]!=self.model.model_id:
|
||||||
|
arr_son[arr_value[self.field_parent]].append([arr_value[self.field_value], self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])])
|
||||||
|
|
||||||
self.create_son(0, arr_son)
|
self.create_son(0, arr_son)
|
||||||
|
|
||||||
|
|
@ -186,6 +283,8 @@ class SelectModelForm(SelectForm):
|
||||||
|
|
||||||
|
|
||||||
def create_son(self, parent_id, arr_son, separator=''):
|
def create_son(self, parent_id, arr_son, separator=''):
|
||||||
|
"""Recursive method for generate parents and children dictionary
|
||||||
|
"""
|
||||||
|
|
||||||
if parent_id in arr_son:
|
if parent_id in arr_son:
|
||||||
for son in arr_son[parent_id]:
|
for son in arr_son[parent_id]:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,25 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os,traceback
|
import os,traceback
|
||||||
import sys, inspect
|
import sys, inspect
|
||||||
|
|
@ -10,11 +30,18 @@ from pathlib import Path
|
||||||
from colorama import init, Fore, Back, Style
|
from colorama import init, Fore, Back, Style
|
||||||
from importlib import import_module, reload
|
from importlib import import_module, reload
|
||||||
from paramecio.cromosoma.webmodel import WebModel
|
from paramecio.cromosoma.webmodel import WebModel
|
||||||
from settings import config
|
sys.path.insert(0, os.path.realpath('.'))
|
||||||
|
|
||||||
#from models import books
|
try:
|
||||||
|
from settings import config
|
||||||
|
except:
|
||||||
|
#print('You need a settings directory with a paramecio2 configuration')
|
||||||
|
#sys.exit(1)
|
||||||
|
pass
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
|
"""Function for create and update mysql tables using webmodel classes and fields how source.
|
||||||
|
"""
|
||||||
|
|
||||||
connection=WebModel.connection()
|
connection=WebModel.connection()
|
||||||
|
|
||||||
|
|
@ -103,6 +130,32 @@ def start():
|
||||||
|
|
||||||
new_tables=[x for x in tables if x not in table_exists]
|
new_tables=[x for x in tables if x not in table_exists]
|
||||||
|
|
||||||
|
# Get foreignkeys
|
||||||
|
|
||||||
|
# SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA='catalogdev_db' AND information_schema.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'FOREIGN KEY' ;
|
||||||
|
|
||||||
|
foreignkey_fields={}
|
||||||
|
|
||||||
|
#| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE |
|
||||||
|
#+--------------------+-------------------+-----------------------------------------+---------------+-------------------+-----------------+
|
||||||
|
#| def | catalogdev_db | product_id_attributesIDX | catalogdev_db | attributes | FOREIGN KEY |
|
||||||
|
|
||||||
|
|
||||||
|
#WebModel.connections
|
||||||
|
|
||||||
|
db_name=WebModel.connections['default']['db']
|
||||||
|
|
||||||
|
with connection.query('SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=%s AND information_schema.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = %s', [db_name, 'FOREIGN KEY']) as cursor:
|
||||||
|
|
||||||
|
for row in cursor:
|
||||||
|
if not row['TABLE_NAME'] in foreignkey_fields:
|
||||||
|
foreignkey_fields[row['TABLE_NAME']]=[]
|
||||||
|
|
||||||
|
foreignkey_fields[row['TABLE_NAME']]=row['CONSTRAINT_NAME'].replace('_{}IDX'.format(row['TABLE_NAME']), '')
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
#If don't want order
|
#If don't want order
|
||||||
#new_tables=set(tables)-set(table_exists)
|
#new_tables=set(tables)-set(table_exists)
|
||||||
|
|
||||||
|
|
@ -160,6 +213,23 @@ def start():
|
||||||
print(Style.BRIGHT+"Checking old versions of model for find changes...")
|
print(Style.BRIGHT+"Checking old versions of model for find changes...")
|
||||||
|
|
||||||
for table in tables:
|
for table in tables:
|
||||||
|
|
||||||
|
#print(table)
|
||||||
|
|
||||||
|
table_fields={table: {}}
|
||||||
|
|
||||||
|
# Field | Type | Null | Key | Default | Extra |
|
||||||
|
#| id | int(11) | NO | PRI | NULL | auto_increment |
|
||||||
|
|
||||||
|
with connection.query('describe %s' % table) as cursor:
|
||||||
|
#all_fields=cursor.fetchall()
|
||||||
|
#print(all_fields)
|
||||||
|
|
||||||
|
for row in cursor:
|
||||||
|
table_fields[table][row['Field']]={'type': row['Type'], 'key': row['Key']}
|
||||||
|
pass
|
||||||
|
#print(table_fields)
|
||||||
|
|
||||||
#connection.query("")
|
#connection.query("")
|
||||||
#Check if new table
|
#Check if new table
|
||||||
|
|
||||||
|
|
@ -182,7 +252,8 @@ def start():
|
||||||
|
|
||||||
for f, v in WebModel.model[table].fields.items():
|
for f, v in WebModel.model[table].fields.items():
|
||||||
|
|
||||||
if not f in WebModel.model[old_table].fields:
|
#if not f in WebModel.model[old_table].fields:
|
||||||
|
if not f in table_fields[table]:
|
||||||
|
|
||||||
fields_to_add.append(f)
|
fields_to_add.append(f)
|
||||||
|
|
||||||
|
|
@ -226,13 +297,15 @@ def start():
|
||||||
|
|
||||||
#Add index
|
#Add index
|
||||||
|
|
||||||
if v.indexed==True and v_old.indexed==False:
|
#if v.indexed==True and v_old.indexed==False:
|
||||||
|
if v.indexed==True and table_fields[table][f]['key']!='MUL':
|
||||||
|
|
||||||
fields_to_add_index.append(f)
|
fields_to_add_index.append(f)
|
||||||
|
|
||||||
changes+=1
|
changes+=1
|
||||||
|
|
||||||
if v.indexed==False and v_old.indexed==True:
|
#if v.indexed==False and v_old.indexed==True:
|
||||||
|
if v.indexed==False and table_fields[table][f]['key']=='MUL' and v.foreignkey==False:
|
||||||
|
|
||||||
fields_to_delete_index.append(f)
|
fields_to_delete_index.append(f)
|
||||||
|
|
||||||
|
|
@ -240,13 +313,15 @@ def start():
|
||||||
|
|
||||||
#Add unique
|
#Add unique
|
||||||
|
|
||||||
if v.unique==True and v_old.unique==False:
|
#if v.unique==True and v_old.unique==False:
|
||||||
|
if v.unique==True and table_fields[table][f]['key']!='UNI':
|
||||||
|
|
||||||
fields_to_add_unique.append(f)
|
fields_to_add_unique.append(f)
|
||||||
|
|
||||||
changes+=1
|
changes+=1
|
||||||
|
|
||||||
if v.unique==False and v_old.unique==True:
|
#if v.unique==False and v_old.unique==True:
|
||||||
|
if v.unique==False and table_fields[table][f]['key']=='UNI':
|
||||||
|
|
||||||
fields_to_delete_unique.append(f)
|
fields_to_delete_unique.append(f)
|
||||||
|
|
||||||
|
|
@ -254,29 +329,43 @@ def start():
|
||||||
|
|
||||||
#Add constraint
|
#Add constraint
|
||||||
|
|
||||||
if v.foreignkey==True and v_old.foreignkey==False:
|
#if v.foreignkey==True and v_old.foreignkey==False:
|
||||||
|
if v.foreignkey==True and table_fields[table][f]['key']!='MUL':
|
||||||
|
|
||||||
fields_to_add_constraint.append(f)
|
fields_to_add_constraint.append(f)
|
||||||
|
|
||||||
changes+=1
|
changes+=1
|
||||||
|
|
||||||
if v.foreignkey==False and v_old.foreignkey==True:
|
#if v.foreignkey==False and v_old.foreignkey==True:
|
||||||
|
if v.foreignkey==False and table_fields[table][f]['key']=='MUL':
|
||||||
|
|
||||||
fields_to_delete_constraint.append(f)
|
if table in foreignkey_fields:
|
||||||
|
|
||||||
changes+=1
|
if f in foreignkey_fields[table]:
|
||||||
|
|
||||||
for f, v in WebModel.model[old_table].fields.items():
|
fields_to_delete_constraint.append(f)
|
||||||
|
|
||||||
|
changes+=1
|
||||||
|
|
||||||
|
# Clean fields
|
||||||
|
|
||||||
|
#for f, v in WebModel.model[old_table].fields.items():
|
||||||
|
|
||||||
|
for f, v in table_fields[table].items():
|
||||||
|
|
||||||
if not f in WebModel.model[table].fields:
|
if not f in WebModel.model[table].fields:
|
||||||
|
|
||||||
#Add constraint
|
#Add constraint
|
||||||
|
|
||||||
if v.foreignkey==True:
|
#if v.foreignkey==True:
|
||||||
|
|
||||||
fields_to_delete_constraint.append(f)
|
if table in foreignkey_fields:
|
||||||
|
|
||||||
changes+=1
|
if f in foreignkey_fields[table]:
|
||||||
|
|
||||||
|
fields_to_delete_constraint.append(f)
|
||||||
|
|
||||||
|
changes+=1
|
||||||
|
|
||||||
fields_to_delete.append(f)
|
fields_to_delete.append(f)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from paramecio.cromosoma.corefields import CharField
|
from paramecio.cromosoma.corefields import CharField
|
||||||
import re
|
import re
|
||||||
|
|
||||||
mail_pattern=re.compile("\w[\w\.-]*@\w[\w\.-]+\.\w+")
|
mail_pattern=re.compile(r"\w[\w\.-]*@\w[\w\.-]+\.\w+")
|
||||||
|
|
||||||
class EmailField(CharField):
|
class EmailField(CharField):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,54 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from paramecio.cromosoma.databases.sqlalchemy import SqlClass
|
||||||
|
from paramecio.cromosoma.coreforms import BaseForm, HiddenForm
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
from importlib import import_module, reload
|
from importlib import import_module, reload
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from paramecio.cromosoma.databases.sqlalchemy import SqlClass
|
|
||||||
from paramecio.cromosoma.coreforms import BaseForm, HiddenForm
|
|
||||||
import copy
|
import copy
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
class PhangoField:
|
class PhangoField:
|
||||||
|
"""Base class for fields used in WebModel classes
|
||||||
|
|
||||||
|
PhangoField is a class with all elements and variables that you can imagine for a database mysql field in a table. You have many types similar to mysql field types.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, size=255, required=False):
|
def __init__(self, name, size=255, required=False):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
name (str): The name of the field
|
||||||
|
size (int): The size of sql field.
|
||||||
|
required (bool): If the field is required or not.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name (str): The name of the field
|
||||||
|
label (str): A label or generic name for use in text labels used for representate the field
|
||||||
|
required (bool): If the field is required or not.
|
||||||
|
size (int): The size of sql field.
|
||||||
|
protected (bool): If the field can be updated or not in WebModel update method.
|
||||||
|
quote_close (str): In old versions was used for get more protection for sql sentences
|
||||||
|
error (bool): If error in query, set to True.
|
||||||
|
txt_error (str): Variable where the basic text error is saved
|
||||||
|
model (str): The model where this component or field live
|
||||||
|
indexed (bool): Property used for set this field how indexed in the database table.
|
||||||
|
unique (bool): Property used for set this field how unique value in the database table.
|
||||||
|
foreignkey (bool): Simple property for make more easy identify foreignkeyfields.
|
||||||
|
default_value (str): Property that define the default value for this field
|
||||||
|
update (bool): Property that define if this field is in an update operation or insert operation
|
||||||
|
check_blank (bool): Property used for check if this value cannot change if is in blank and is filled
|
||||||
|
name_form(BaseForm): Define the form, when is created forms with create_forms you can change the properties of this class
|
||||||
|
escape (bool): Property that define if make escape in show_formatted. This property control the html transformation of <>" characters in html entities.If false, convert.
|
||||||
|
file_related (bool): File related: if the field have a file related, delete the file
|
||||||
|
extra_parameters (list): Extra parameters for the form related with this field
|
||||||
|
t (PTemplate): Template manager for the form if needed
|
||||||
|
error_default (str): Error text by default
|
||||||
|
show_formatted_value (bool): Show this value formatted
|
||||||
|
help (str): Value used for help strings in tooltips in forms
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
# The name of the field in database table
|
# The name of the field in database table
|
||||||
|
|
||||||
|
|
@ -50,7 +86,7 @@ class PhangoField:
|
||||||
|
|
||||||
self.txt_error=""
|
self.txt_error=""
|
||||||
|
|
||||||
# Themodel where this component or field live
|
# The model where this component or field live
|
||||||
|
|
||||||
self.model=None
|
self.model=None
|
||||||
|
|
||||||
|
|
@ -100,7 +136,7 @@ class PhangoField:
|
||||||
|
|
||||||
# Error by default
|
# Error by default
|
||||||
|
|
||||||
self.error_default='Error: field required'
|
self.error_default='Error: '+self.name+' field required'
|
||||||
|
|
||||||
# Show this value formatted
|
# Show this value formatted
|
||||||
|
|
||||||
|
|
@ -109,28 +145,29 @@ class PhangoField:
|
||||||
# Value used for help strings in tooltips in forms
|
# Value used for help strings in tooltips in forms
|
||||||
|
|
||||||
self.help=''
|
self.help=''
|
||||||
|
|
||||||
# This method is used for describe the new field in a sql language format.
|
self.type_sql='varchar({})'.format(self.size)
|
||||||
|
|
||||||
|
|
||||||
def get_type_sql(self):
|
def get_type_sql(self):
|
||||||
|
"""This method is used for describe the new field in a sql language format."""
|
||||||
|
|
||||||
return 'VARCHAR('+str(self.size)+') NOT NULL DEFAULT "'+self.default_value+'"'
|
return 'VARCHAR('+str(self.size)+') NOT NULL DEFAULT "'+self.default_value+'"'
|
||||||
|
|
||||||
def show_formatted(self, value):
|
def show_formatted(self, value):
|
||||||
|
"""Method for format the value to show in html or others text outputs"""
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# This method for check the value
|
|
||||||
|
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value):
|
||||||
|
"""Method for check if value is valid for this type field"""
|
||||||
|
|
||||||
self.error=False
|
self.error=False
|
||||||
self.txt_error=''
|
self.txt_error=''
|
||||||
|
|
||||||
value=str(value).strip()
|
value=str(value).strip()
|
||||||
|
|
||||||
|
#Minimal escape for prevent basic js injection.
|
||||||
|
|
||||||
if self.escape==False:
|
if self.escape==False:
|
||||||
value=value.replace('<', '<')
|
value=value.replace('<', '<')
|
||||||
|
|
||||||
|
|
@ -150,6 +187,7 @@ class PhangoField:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def create_form(self):
|
def create_form(self):
|
||||||
|
"""Create a BaseForm object for use in forms functions and methods"""
|
||||||
#self.name, self.default_value,
|
#self.name, self.default_value,
|
||||||
|
|
||||||
final_parameters=copy.copy(self.extra_parameters)
|
final_parameters=copy.copy(self.extra_parameters)
|
||||||
|
|
@ -165,6 +203,7 @@ class PhangoField:
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def change_form(self, new_form, parameters):
|
def change_form(self, new_form, parameters):
|
||||||
|
"""Change the base form of the field and its parameters"""
|
||||||
|
|
||||||
self.name_form=new_form
|
self.name_form=new_form
|
||||||
|
|
||||||
|
|
@ -174,6 +213,10 @@ class PhangoField:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class PrimaryKeyField(PhangoField):
|
class PrimaryKeyField(PhangoField):
|
||||||
|
"""Primary key field based in PhangoField.
|
||||||
|
|
||||||
|
This field is used for create a typical id field in a mariadb/mysql table
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, size=11, required=False):
|
def __init__(self, name, size=11, required=False):
|
||||||
super(PrimaryKeyField, self).__init__(name, size, required)
|
super(PrimaryKeyField, self).__init__(name, size, required)
|
||||||
|
|
@ -181,6 +224,7 @@ class PrimaryKeyField(PhangoField):
|
||||||
self.name_form=HiddenForm
|
self.name_form=HiddenForm
|
||||||
self.required=False
|
self.required=False
|
||||||
self.error_default="The value is zero"
|
self.error_default="The value is zero"
|
||||||
|
self.type_sql='int({})'.format(self.size)
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value):
|
||||||
|
|
||||||
|
|
@ -217,6 +261,24 @@ class PrimaryKeyField(PhangoField):
|
||||||
|
|
||||||
|
|
||||||
class WebModel:
|
class WebModel:
|
||||||
|
"""The most important class for the framework
|
||||||
|
|
||||||
|
Webmodel is a class for create objects that represent models. This models are a mirage of SQL tables. You can create fields, add indexes, foreign keys, and more.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
arr_sql_index (dict): Internal dict used for generate mysql index in fields
|
||||||
|
arr_sql_set_index (dict): Internal dict used for generate mysql index in fields
|
||||||
|
arr_sql_unique (dict): Internal dict used for generate mysql unique values in fields
|
||||||
|
arr_sql_set_unique (dict): Internal dict used for generate mysql unique values in fields
|
||||||
|
last_query (str): The last query execute by WebModel
|
||||||
|
connection_pool (list): A list used in older versions, deprecated
|
||||||
|
first_primary_key (PrimaryKeyField): Field used for primary field and create models in database.
|
||||||
|
model (OrderedDict): Dict used for internal things and create tables.
|
||||||
|
connections (dict): A dict with the configuration of the mysql connection. You can use this element in config.py. You set elements, normally "default" with typical elements how:
|
||||||
|
host: Database host, user: The username of mysql db, password: The password of user, db: The name of the db, charset: The charset of database, normally utf8, db_type: The db_type, possible values are mysqldb or pymysql, by default, pymysql.
|
||||||
|
connection_id (str): The id by default of the selected connection from connections.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
__slots__=('sqlclass', 'fields', 'forms')
|
__slots__=('sqlclass', 'fields', 'forms')
|
||||||
|
|
||||||
|
|
@ -248,12 +310,53 @@ class WebModel:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def connection():
|
def connection():
|
||||||
|
"""Static method for make a connection using SqlClass
|
||||||
|
|
||||||
|
Returns: Return a SqlClass connection for mysql db.
|
||||||
|
"""
|
||||||
|
|
||||||
return SqlClass(WebModel.connections['default'])
|
return SqlClass(WebModel.connections['default'])
|
||||||
|
|
||||||
# Init the class
|
# Init the class
|
||||||
|
|
||||||
def __init__(self, sqlclass=None, name_field_id="id"):
|
def __init__(self, sqlclass=None, name_field_id="id"):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
sqlclass (SqlClass): The SqlClass connection used for the mysql db
|
||||||
|
name_field_id (str): The name of field id of this model/mysql table
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
name (str): The name of this model correspondient to the sql table name with lower string.
|
||||||
|
label (str): Descriptive name, first is used self.name how default.
|
||||||
|
label_general (str): Descriptive general name, first is used self.name how default.
|
||||||
|
name_field_id (str): The name of field id of this model/mysql table
|
||||||
|
fields (OrderedDict): A dict with the fields of model/table based in PhangoField objects.
|
||||||
|
fields_error (OrderedDict): A dict where the errors when check data fields are saved
|
||||||
|
related (list): A list where related fields are saved.
|
||||||
|
forms (OrderedDict): A dict where forms related with fields using how base BaseForm class are saved if you use self.create_forms() method.
|
||||||
|
errors (dict): A dict where generic errors are saved.
|
||||||
|
num_errors (int): Number of errors generated by the model on query methods.
|
||||||
|
query_error (str): If error in query, saved here.
|
||||||
|
values_query (list): Where the values for a sql query for filtering are saved.
|
||||||
|
conditions (list): A list used for define the sql conditions.
|
||||||
|
First element is the sql condition, Example: 'WHERE id=%s', and second element is the variable to substitute %s, example [1]. Complete example: ['WHERE id=%s', 1]
|
||||||
|
order_by (str): Internal variable used for set the sql order str. You don't shoud change this variable if yo don't know what are you doing.
|
||||||
|
limit (str): Internal variable used for set the sql limit str.
|
||||||
|
related_models_deleted (list): Internal variable used for delete tables from db.
|
||||||
|
required_save (str): Internal variable used for required fields defined in self.fields
|
||||||
|
primary_key (str): Default name of primary key field
|
||||||
|
yes_reset_conditions (bool): If True, methods how select and update reset self.conditions. If False, self.conditions is used in next select and update executions.
|
||||||
|
updated (bool): True if the model is used for update, False if the model is used for insert or other operations.
|
||||||
|
valid_fields (list): List with the fields validated for insert or update
|
||||||
|
last_id (int): The id of last inserted row.
|
||||||
|
distinct (str): Add DISTINCT keyword to self.select method.
|
||||||
|
post (dict): A simple dictionary where post values are saved for use of fields classes
|
||||||
|
files_delete (dict): A simple dictionary that save the fields that have files related. If i delete the row in database i need delete the files related
|
||||||
|
sqlclass (SqlClass): A sql_class used for connect to db.
|
||||||
|
show_formatted (bool): If True, by default all fields are showed with formatted value using show_formatted method of PhangoField classes and children in select method. If False, raw value is showed.
|
||||||
|
enctype (bool): If True, forms generated using this model are prepared for enctype=multipart/form-data A.K.A. upload files.
|
||||||
|
model_id (int): Variable where the actual row from model selected can be saved for different things.
|
||||||
|
"""
|
||||||
|
|
||||||
self.cached=WebModel.global_cached
|
self.cached=WebModel.global_cached
|
||||||
|
|
||||||
|
|
@ -277,7 +380,7 @@ class WebModel:
|
||||||
|
|
||||||
# Errors of fields of the table, for safe thread reasons.
|
# Errors of fields of the table, for safe thread reasons.
|
||||||
|
|
||||||
self.fields_error=OrderedDict()
|
#self.fields_error=OrderedDict()
|
||||||
|
|
||||||
#The tables related with foreignkeyfield to this table
|
#The tables related with foreignkeyfield to this table
|
||||||
|
|
||||||
|
|
@ -357,12 +460,18 @@ class WebModel:
|
||||||
|
|
||||||
self.enctype=False
|
self.enctype=False
|
||||||
|
|
||||||
|
self.model_id=0
|
||||||
|
|
||||||
self.dummy=0
|
self.dummy=0
|
||||||
|
|
||||||
# A method for add the connection
|
# A method for add the connection
|
||||||
|
|
||||||
def conn(self, sqlclass):
|
def conn(self, sqlclass):
|
||||||
|
""" Method for get the SqlClass object and prepare sql variables
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sqlclass (SqlClass): A SqlClass object that present the db connection
|
||||||
|
"""
|
||||||
self.sqlclass=sqlclass
|
self.sqlclass=sqlclass
|
||||||
|
|
||||||
# Reset conditions
|
# Reset conditions
|
||||||
|
|
@ -378,6 +487,11 @@ class WebModel:
|
||||||
# A method for change the name of table
|
# A method for change the name of table
|
||||||
|
|
||||||
def change_name(self, name):
|
def change_name(self, name):
|
||||||
|
""" A method for change the name of table
|
||||||
|
|
||||||
|
Args;
|
||||||
|
name (str): The new name of table
|
||||||
|
"""
|
||||||
|
|
||||||
self.name=name
|
self.name=name
|
||||||
|
|
||||||
|
|
@ -386,6 +500,7 @@ class WebModel:
|
||||||
# A method where create the new fields of this model
|
# A method where create the new fields of this model
|
||||||
|
|
||||||
def create_fields(self):
|
def create_fields(self):
|
||||||
|
"""Dummy method for use in children classes for add fields"""
|
||||||
|
|
||||||
#print([i for i in dir(self.__class__) if i[:1] != '_'])
|
#print([i for i in dir(self.__class__) if i[:1] != '_'])
|
||||||
#print(dir(self))
|
#print(dir(self))
|
||||||
|
|
@ -395,6 +510,14 @@ class WebModel:
|
||||||
# A method for register the fields
|
# A method for register the fields
|
||||||
|
|
||||||
def register(self, field_model, required=False):
|
def register(self, field_model, required=False):
|
||||||
|
"""A method for register the fields in model class
|
||||||
|
|
||||||
|
With this method, your register your fields in the model, inside self.fields attribute. Fields are used for build the query for get the data from the sql table.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
field_model (PhangoField): PhangoField object for add to model
|
||||||
|
required (bool): If True, the field is required when you insert or update a item row in table model. If False, the field is not required. If field is not required and checking fail, the model update/insert ignore it.
|
||||||
|
"""
|
||||||
|
|
||||||
#self.fields_required[field_model]=field_model.required
|
#self.fields_required[field_model]=field_model.required
|
||||||
|
|
||||||
|
|
@ -436,6 +559,13 @@ class WebModel:
|
||||||
# Method for make queries
|
# Method for make queries
|
||||||
|
|
||||||
def query(self, str_query, args=[], connection_id='default'):
|
def query(self, str_query, args=[], connection_id='default'):
|
||||||
|
"""Method for make typical sql query to db
|
||||||
|
|
||||||
|
Args:
|
||||||
|
str_query (str): The str query. Use the typical format of sql python drivers, example: select * from my_table WHERE id=%s.
|
||||||
|
args (list): The arguments to substitute %s characters of the strings. The list must sequential with %s characters in the string.
|
||||||
|
connection_id (str): The connection data used for this connection, by default is "default".
|
||||||
|
"""
|
||||||
|
|
||||||
self.connect_to_db()
|
self.connect_to_db()
|
||||||
return self.sqlclass.query(str_query, args, connection_id)
|
return self.sqlclass.query(str_query, args, connection_id)
|
||||||
|
|
@ -443,6 +573,8 @@ class WebModel:
|
||||||
# Method for clean fields
|
# Method for clean fields
|
||||||
|
|
||||||
def clean_fields(self):
|
def clean_fields(self):
|
||||||
|
"""Method for delete fields from self.fields dict"""
|
||||||
|
|
||||||
clean=self.fields_to_clean
|
clean=self.fields_to_clean
|
||||||
for field in self.fields_to_clean:
|
for field in self.fields_to_clean:
|
||||||
del self.fields[field]
|
del self.fields[field]
|
||||||
|
|
@ -451,6 +583,14 @@ class WebModel:
|
||||||
# External agent define if the update is in code or from external source, how a form.
|
# External agent define if the update is in code or from external source, how a form.
|
||||||
|
|
||||||
def insert(self, dict_values, external_agent=True):
|
def insert(self, dict_values, external_agent=True):
|
||||||
|
"""Insert method, for insert a row in database using a dictionary
|
||||||
|
|
||||||
|
This method is a shortcut for typical sql insert sentence.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dict_values (dict): A dict with the name of the fields how defined in PhangoField for the keys, and values for the values designed for every field.
|
||||||
|
external_agent (bool): External agent define if the update is in code or from external source, how a form.
|
||||||
|
"""
|
||||||
|
|
||||||
self.clean_fields()
|
self.clean_fields()
|
||||||
|
|
||||||
|
|
@ -466,7 +606,6 @@ class WebModel:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
#fields, values, update_values=self.check_all_fields(dict_values, external_agent)
|
|
||||||
arr_return=self.check_all_fields(dict_values, external_agent)
|
arr_return=self.check_all_fields(dict_values, external_agent)
|
||||||
|
|
||||||
if arr_return:
|
if arr_return:
|
||||||
|
|
@ -476,11 +615,10 @@ class WebModel:
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except:
|
except Exception as e:
|
||||||
self.query_error='Cannot insert the new row'
|
self.query_error='Cannot insert the new row '+str(e)
|
||||||
print(sys.exc_info()[0])
|
#print(sys.exc_info()[0])
|
||||||
raise
|
return False
|
||||||
#return False
|
|
||||||
|
|
||||||
c=len(values)
|
c=len(values)
|
||||||
|
|
||||||
|
|
@ -509,6 +647,15 @@ class WebModel:
|
||||||
# Update method. For update one or many rows.
|
# Update method. For update one or many rows.
|
||||||
|
|
||||||
def update(self, dict_values, external_agent=True):
|
def update(self, dict_values, external_agent=True):
|
||||||
|
"""Upate method, for update a row in database using a dictionary
|
||||||
|
|
||||||
|
This method is a shortcut for typical sql update sentence.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dict_values (dict): A dict with the name of the fields how defined in PhangoField for the keys, and values for the values designed for every field.
|
||||||
|
external_agent (bool): External agent define if the update is in code or from external source, how a form.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
self.clean_fields()
|
self.clean_fields()
|
||||||
|
|
||||||
|
|
@ -583,7 +730,7 @@ class WebModel:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def reset_conditions(self):
|
def reset_conditions(self):
|
||||||
|
"""Method for reset self.conditions to default values"""
|
||||||
self.conditions=["WHERE 1=1", []]
|
self.conditions=["WHERE 1=1", []]
|
||||||
self.limit=''
|
self.limit=''
|
||||||
|
|
||||||
|
|
@ -591,6 +738,19 @@ class WebModel:
|
||||||
#Type assoc can be assoc for return dictionaries
|
#Type assoc can be assoc for return dictionaries
|
||||||
|
|
||||||
def select(self, arr_select=[], raw_query=False):
|
def select(self, arr_select=[], raw_query=False):
|
||||||
|
"""A method for select fields from a table in db. Support for foreignkeys.
|
||||||
|
|
||||||
|
This method is a shortcut for typical sql select sentence. You can select multiple tables using ForeignKeyField class.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
arr_select (dict): A list with the name of the fields how defined in PhangoField.
|
||||||
|
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
false (bool): If return false, the db connection is down.
|
||||||
|
sql_cursor (cursor): Return cursor db for get data using loops or other if operation is successful, if not, return False.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
self.clean_fields()
|
self.clean_fields()
|
||||||
|
|
||||||
|
|
@ -706,14 +866,29 @@ class WebModel:
|
||||||
# Show results in a dictionary
|
# Show results in a dictionary
|
||||||
|
|
||||||
def fetch(self, cursor):
|
def fetch(self, cursor):
|
||||||
|
""" Simple method for get a row from db using cursor
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cursor (Db cursor): A typical db cursor of python sql interface standard.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
row (dict): Return a dictionary with the row selected.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
return cursor.fetchone()
|
return cursor.fetchone()
|
||||||
|
|
||||||
def insert_id(self):
|
def insert_id(self):
|
||||||
|
"""Method for get the id from last row inserted in table"""
|
||||||
|
|
||||||
return self.last_id
|
return self.last_id
|
||||||
|
|
||||||
def element_exists(self, id):
|
def element_exists(self, id):
|
||||||
|
"""Check if exist row with id in db
|
||||||
|
|
||||||
|
Args:
|
||||||
|
id (int): The id of the row to search.
|
||||||
|
"""
|
||||||
|
|
||||||
self.conditions=['WHERE `'+self.name_field_id+'`=%s', [id]]
|
self.conditions=['WHERE `'+self.name_field_id+'`=%s', [id]]
|
||||||
|
|
||||||
|
|
@ -731,6 +906,15 @@ class WebModel:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def select_a_row(self, id, fields_selected=[], raw_query=0):
|
def select_a_row(self, id, fields_selected=[], raw_query=0):
|
||||||
|
"""Shortcut for get a simple row from a query
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
|
||||||
|
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
row (dict): Returns dict with the row values.
|
||||||
|
"""
|
||||||
|
|
||||||
self.conditions=['WHERE `'+self.name+'`.`'+self.name_field_id+'`=%s', [id]]
|
self.conditions=['WHERE `'+self.name+'`.`'+self.name_field_id+'`=%s', [id]]
|
||||||
|
|
||||||
|
|
@ -752,6 +936,15 @@ class WebModel:
|
||||||
return row
|
return row
|
||||||
|
|
||||||
def select_a_row_where(self, fields_selected=[], raw_query=0, begin=0):
|
def select_a_row_where(self, fields_selected=[], raw_query=0, begin=0):
|
||||||
|
"""Shortcut for get a simple row from a query using self.conditions
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
|
||||||
|
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
row (dict): Returns dict with the row values.
|
||||||
|
"""
|
||||||
|
|
||||||
self.limit="limit "+str(begin)+", 1"
|
self.limit="limit "+str(begin)+", 1"
|
||||||
|
|
||||||
|
|
@ -770,6 +963,16 @@ class WebModel:
|
||||||
|
|
||||||
|
|
||||||
def select_to_array(self, fields_selected=[], raw_query=0):
|
def select_to_array(self, fields_selected=[], raw_query=0):
|
||||||
|
"""Shortcut for get a a list of rows from select sql query
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
|
||||||
|
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
row_values (dict): Returns dict with the row values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
if len(fields_selected)==0:
|
if len(fields_selected)==0:
|
||||||
fields_selected=list(self.fields.keys())
|
fields_selected=list(self.fields.keys())
|
||||||
|
|
@ -810,6 +1013,16 @@ class WebModel:
|
||||||
|
|
||||||
def select_to_dict(self, fields_selected=[], raw_query=0, integer=True):
|
def select_to_dict(self, fields_selected=[], raw_query=0, integer=True):
|
||||||
|
|
||||||
|
"""Shortcut for get a dict of rows from select sql query
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
|
||||||
|
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
row (dict): Returns dict with the row values.
|
||||||
|
"""
|
||||||
|
|
||||||
if integer:
|
if integer:
|
||||||
def check_index(index):
|
def check_index(index):
|
||||||
return index
|
return index
|
||||||
|
|
@ -855,6 +1068,15 @@ class WebModel:
|
||||||
# A method por count num rows affected for sql conditions
|
# A method por count num rows affected for sql conditions
|
||||||
|
|
||||||
def select_count(self, field_to_count='id', raw_query=True):
|
def select_count(self, field_to_count='id', raw_query=True):
|
||||||
|
"""Method for get a typical sql count using conditions
|
||||||
|
|
||||||
|
Args:
|
||||||
|
field_to_count (str): The field
|
||||||
|
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
num_elecments (int): Returns the number of elements selected.
|
||||||
|
"""
|
||||||
|
|
||||||
# Connect to db
|
# Connect to db
|
||||||
|
|
||||||
|
|
@ -901,12 +1123,19 @@ class WebModel:
|
||||||
# A method for delete rows using sql conditions
|
# A method for delete rows using sql conditions
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
"""Method for delete a series of rows using conditions
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool (bool): If delete is successfully, return True, if not, return False.
|
||||||
|
"""
|
||||||
|
|
||||||
#self.connect_to_db()
|
#self.connect_to_db()
|
||||||
|
|
||||||
#Need delete rows from other related tables save in self.related_models_deleted
|
#Need delete rows from other related tables save in self.related_models_deleted
|
||||||
|
|
||||||
sql=("delete from `"+self.name+"` "+self.conditions[0]+' '+self.order_by+' '+self.limit).strip()
|
#+' '+self.order_by+' '+self.limit
|
||||||
|
|
||||||
|
sql=("delete from `"+self.name+"` "+self.conditions[0]).strip()
|
||||||
|
|
||||||
result=self.query(sql, self.conditions[1], self.connection_id)
|
result=self.query(sql, self.conditions[1], self.connection_id)
|
||||||
|
|
||||||
|
|
@ -923,6 +1152,15 @@ class WebModel:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def set_conditions(self, sql_text, values:list) -> object:
|
def set_conditions(self, sql_text, values:list) -> object:
|
||||||
|
"""Method for set conditions for a typical sql query
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sql_text (str): The sql text with the conditions, Example: WHERE id=%s
|
||||||
|
values (list): A list with values for substitute %s characters for the real values filtered for not allow sql injections.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Return the same object with self.conditions modified.
|
||||||
|
"""
|
||||||
|
|
||||||
self.conditions=[sql_text, values]
|
self.conditions=[sql_text, values]
|
||||||
|
|
||||||
|
|
@ -930,6 +1168,15 @@ class WebModel:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_in_list(in_list):
|
def check_in_list(in_list):
|
||||||
|
"""Method for convert values to int for use in IN (1,2,3) sql sentences.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
in_list (list): List with numbers items.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
sql_filtered (str): with (1,2,3) sql sentence filtered.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
for x in range(0, len(in_list)):
|
for x in range(0, len(in_list)):
|
||||||
try:
|
try:
|
||||||
|
|
@ -939,6 +1186,15 @@ class WebModel:
|
||||||
return '('+', '.join(in_list)+')'
|
return '('+', '.join(in_list)+')'
|
||||||
|
|
||||||
def check_in_list_str(self, field, in_list):
|
def check_in_list_str(self, field, in_list):
|
||||||
|
"""Method for convert values to int for use in IN (value1, value2, value3) sql sentences.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
field (PhangoField): The PhangoField used for check the values of in_list
|
||||||
|
in_list (list): List with value items.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
sql_filtered (str): (value1, value2, value3) sql sentence filtered.
|
||||||
|
"""
|
||||||
|
|
||||||
for x in range(0, len(in_list)):
|
for x in range(0, len(in_list)):
|
||||||
in_list[x]=str(self.fields[field].check(in_list[x]))
|
in_list[x]=str(self.fields[field].check(in_list[x]))
|
||||||
|
|
@ -946,6 +1202,15 @@ class WebModel:
|
||||||
return '("'+'", "'.join(in_list)+'")'
|
return '("'+'", "'.join(in_list)+'")'
|
||||||
|
|
||||||
def set_order(self, order:dict) -> object:
|
def set_order(self, order:dict) -> object:
|
||||||
|
""" Method for set and complete the query with "order by" sentences.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
order (dict): A dict with a field name how key, and 0 or 1 how values. 0 define order how ASC, 1 define order how DESC.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
Returns the same object for execute a query after set_order declaration.
|
||||||
|
"""
|
||||||
|
|
||||||
arr_order=[]
|
arr_order=[]
|
||||||
arr_order.append('ASC')
|
arr_order.append('ASC')
|
||||||
|
|
@ -964,6 +1229,15 @@ class WebModel:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_limit(self, limit: tuple) -> None:
|
def set_limit(self, limit: tuple) -> None:
|
||||||
|
""" Method for set and complete the query with "limit" sentences.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
limit (tuple): A tuple with one or two elements. If one element, example (1), the result is "LIMIT first_element", if two elements, example (1,2), the result is "LIMIT first_element, two_element"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
Returns the same object for execute a query after set_order declaration.
|
||||||
|
"""
|
||||||
|
|
||||||
limit[0]=int(limit[0])
|
limit[0]=int(limit[0])
|
||||||
|
|
||||||
|
|
@ -979,6 +1253,7 @@ class WebModel:
|
||||||
# Method for create sql tables
|
# Method for create sql tables
|
||||||
|
|
||||||
def create_table(self):
|
def create_table(self):
|
||||||
|
"""Method for create a table from this model object"""
|
||||||
|
|
||||||
#self.connect_to_db()
|
#self.connect_to_db()
|
||||||
|
|
||||||
|
|
@ -1089,6 +1364,12 @@ class WebModel:
|
||||||
# Method for drop sql tables and related
|
# Method for drop sql tables and related
|
||||||
|
|
||||||
def drop(self):
|
def drop(self):
|
||||||
|
"""Method for drop a table based in this model
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
sql_str (str): Return the sql query for drop the table represented by this model
|
||||||
|
"""
|
||||||
|
|
||||||
return self.query('DROP TABLE '+self.name, [], self.connection_id)
|
return self.query('DROP TABLE '+self.name, [], self.connection_id)
|
||||||
|
|
||||||
#Return an array with all fields
|
#Return an array with all fields
|
||||||
|
|
@ -1099,6 +1380,20 @@ class WebModel:
|
||||||
#Check of all fields in table.
|
#Check of all fields in table.
|
||||||
|
|
||||||
def check_all_fields(self, dict_values, external_agent, yes_update=False, errors_set="insert"):
|
def check_all_fields(self, dict_values, external_agent, yes_update=False, errors_set="insert"):
|
||||||
|
"""Method for check all fields of a model for insert or update a row in table db.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dict_values (dict): The dict of values to check
|
||||||
|
external_agent (bool): If True, the query is considered manipulated by external agent and the checks are stricts, if not, checks are not stricts
|
||||||
|
yes_update (bool): If True, the check need be done for update sql sentence, if False, the check is done for insert sql sentence
|
||||||
|
errors_set (str): If insert value, the errors are set for insert sql statement, if update value, then the errors are set for update sql statement.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
wrong (bool): Return False if checking is wrong. If not False returns a tuple with fields filtered, original values as values and values filtered how update_values
|
||||||
|
fields (list): list with fields
|
||||||
|
values (dict): dict with values
|
||||||
|
update_values (dict): dict with updated values with checking
|
||||||
|
"""
|
||||||
|
|
||||||
fields=[]
|
fields=[]
|
||||||
values=[]
|
values=[]
|
||||||
|
|
@ -1227,6 +1522,7 @@ class WebModel:
|
||||||
#Reset the require field in fields
|
#Reset the require field in fields
|
||||||
|
|
||||||
def reset_require(self):
|
def reset_require(self):
|
||||||
|
"""Reset the require attribute in fields"""
|
||||||
|
|
||||||
for k, v in self.fields.items():
|
for k, v in self.fields.items():
|
||||||
|
|
||||||
|
|
@ -1237,6 +1533,7 @@ class WebModel:
|
||||||
#Reload the require field in fields
|
#Reload the require field in fields
|
||||||
|
|
||||||
def reload_require(self):
|
def reload_require(self):
|
||||||
|
"""Reload the require field in fields"""
|
||||||
|
|
||||||
for k,r in self.fields.items():
|
for k,r in self.fields.items():
|
||||||
self.fields[k].required=r
|
self.fields[k].required=r
|
||||||
|
|
@ -1244,6 +1541,7 @@ class WebModel:
|
||||||
#Choose all fields to updated
|
#Choose all fields to updated
|
||||||
|
|
||||||
def set_valid_fields(self, fields={}):
|
def set_valid_fields(self, fields={}):
|
||||||
|
"""Choose all fields to updated"""
|
||||||
|
|
||||||
if len(fields)==0:
|
if len(fields)==0:
|
||||||
fields=self.fields.keys()
|
fields=self.fields.keys()
|
||||||
|
|
@ -1253,6 +1551,7 @@ class WebModel:
|
||||||
#Create a form based in table.
|
#Create a form based in table.
|
||||||
|
|
||||||
def create_forms(self, arr_fields=[]):
|
def create_forms(self, arr_fields=[]):
|
||||||
|
"""Create a form based in table."""
|
||||||
|
|
||||||
self.forms=OrderedDict()
|
self.forms=OrderedDict()
|
||||||
|
|
||||||
|
|
@ -1270,6 +1569,12 @@ class WebModel:
|
||||||
return arr_fields
|
return arr_fields
|
||||||
|
|
||||||
def create_form_after(self, form_after, new_form):
|
def create_form_after(self, form_after, new_form):
|
||||||
|
"""Create form after other form
|
||||||
|
|
||||||
|
Args:
|
||||||
|
form_after (str): The name of the form where the new form is located next
|
||||||
|
new_form (BaseForm): The BaseForm or derivated class used for create the new form.
|
||||||
|
"""
|
||||||
|
|
||||||
new_dict=OrderedDict()
|
new_dict=OrderedDict()
|
||||||
|
|
||||||
|
|
@ -1281,6 +1586,11 @@ class WebModel:
|
||||||
self.forms=new_dict
|
self.forms=new_dict
|
||||||
|
|
||||||
def show_errors(self):
|
def show_errors(self):
|
||||||
|
"""Get all errors of model last operation.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
error_txt (str): A string with all errors.
|
||||||
|
"""
|
||||||
|
|
||||||
arr_error=[]
|
arr_error=[]
|
||||||
error_txt=''
|
error_txt=''
|
||||||
|
|
@ -1301,6 +1611,11 @@ class WebModel:
|
||||||
return error_txt
|
return error_txt
|
||||||
|
|
||||||
def collect_errors(self):
|
def collect_errors(self):
|
||||||
|
"""Get all errors and save in dictionary
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
errors (dict): Return a dict where the key is the field where the error exists and value is the error text.
|
||||||
|
"""
|
||||||
|
|
||||||
arr_error= {}
|
arr_error= {}
|
||||||
error_txt=''
|
error_txt=''
|
||||||
|
|
@ -1318,12 +1633,15 @@ class WebModel:
|
||||||
return arr_error
|
return arr_error
|
||||||
|
|
||||||
def safe_query(self):
|
def safe_query(self):
|
||||||
|
"""Method for reset require for fields.
|
||||||
|
With this method you can make queries without real checks, except mysql injection safe variables."""
|
||||||
|
|
||||||
self.create_forms()
|
self.create_forms()
|
||||||
self.reset_require()
|
self.reset_require()
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""Method for close sqlclass db connection"""
|
||||||
|
|
||||||
self.sqlclass.close()
|
self.sqlclass.close()
|
||||||
|
|
||||||
|
|
@ -1341,6 +1659,7 @@ class WebModel:
|
||||||
#del sqlclass.connection[key]
|
#del sqlclass.connection[key]
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def escape_sql(value):
|
def escape_sql(value):
|
||||||
|
"""Manual escape for sql, you shouldn't use it"""
|
||||||
|
|
||||||
value=str(value)
|
value=str(value)
|
||||||
|
|
||||||
|
|
@ -1354,6 +1673,11 @@ class WebModel:
|
||||||
# Set post values from a post array
|
# Set post values from a post array
|
||||||
|
|
||||||
def set_post_values(self, post):
|
def set_post_values(self, post):
|
||||||
|
"""Prepare a dict with values using fields keys how base
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
post (dict): Return a dict with values without checking anything.
|
||||||
|
"""
|
||||||
|
|
||||||
for k in self.fields.keys():
|
for k in self.fields.keys():
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from settings import config
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from paramecio.cromosoma.webmodel import WebModel
|
from paramecio.cromosoma.webmodel import WebModel
|
||||||
from paramecio.citoplasma.datetime import set_timezone
|
from paramecio.citoplasma.datetime import set_timezone
|
||||||
from itsdangerous import JSONWebSignatureSerializer
|
#from itsdangerous import JSONWebSignatureSerializer
|
||||||
from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
||||||
from paramecio.wsgiapp import app
|
from paramecio.wsgiapp import app
|
||||||
#from paramecio.citoplasma.sessions import after_session
|
#from paramecio.citoplasma.sessions import after_session
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from paramecio.modules.admin.models.admin import UserAdmin
|
||||||
from paramecio.citoplasma.i18n import load_lang, I18n
|
from paramecio.citoplasma.i18n import load_lang, I18n
|
||||||
from paramecio.citoplasma.urls import make_url, add_get_parameters, redirect
|
from paramecio.citoplasma.urls import make_url, add_get_parameters, redirect
|
||||||
from paramecio.citoplasma.sessions import get_session, generate_session
|
from paramecio.citoplasma.sessions import get_session, generate_session
|
||||||
from bottle import get,post,response,request
|
from bottle import get,post,response,request, Bottle
|
||||||
from settings import config
|
from settings import config
|
||||||
from settings import config_admin
|
from settings import config_admin
|
||||||
from paramecio.citoplasma.adminutils import get_menu, get_language, make_admin_url, check_login, login_model
|
from paramecio.citoplasma.adminutils import get_menu, get_language, make_admin_url, check_login, login_model
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,9 @@ theme='default'
|
||||||
#base_modules="modules"
|
#base_modules="modules"
|
||||||
|
|
||||||
#Type server used for connect to the internet...
|
#Type server used for connect to the internet...
|
||||||
|
# With bottle 0.13 is better use gunicorn
|
||||||
|
|
||||||
server_used="cherrypy"
|
server_used="gunicorn"
|
||||||
|
|
||||||
#Module showed in index
|
#Module showed in index
|
||||||
|
|
||||||
|
|
@ -135,11 +136,3 @@ yes_static=True
|
||||||
#Database mysql config, if you want anything...
|
#Database mysql config, if you want anything...
|
||||||
|
|
||||||
#WebModel.connections={'default': {'name': 'default', 'host': 'localhost', 'user': 'root', 'password': '', 'db': 'example', 'charset': 'utf8mb4', 'set_connection': False} }
|
#WebModel.connections={'default': {'name': 'default', 'host': 'localhost', 'user': 'root', 'password': '', 'db': 'example', 'charset': 'utf8mb4', 'set_connection': False} }
|
||||||
|
|
||||||
#Please dont change this line if you don't know what you are doing
|
|
||||||
|
|
||||||
for module in modules:
|
|
||||||
module+='.settings.config'
|
|
||||||
module_path=module.replace('.','/')+'.py'
|
|
||||||
if os.path.isfile(module_path):
|
|
||||||
mod=import_module(module)
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
# Principal app
|
||||||
|
|
||||||
import bottle
|
import bottle
|
||||||
|
|
||||||
app=bottle.app()
|
app=bottle.app()
|
||||||
|
|
|
||||||
23
setup.py
23
setup.py
|
|
@ -5,32 +5,32 @@ import os
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info < (3, 3):
|
if sys.version_info < (3, 8):
|
||||||
raise NotImplementedError("Sorry, you need at least Python 3.3 for use paramecio.")
|
raise NotImplementedError("Sorry, you need at least Python 3.8 for use paramecio.")
|
||||||
|
|
||||||
#import paramecio
|
#import paramecio
|
||||||
# Pillow should be installed after if you need ImageField
|
# Pillow should be installed after if you need ImageField
|
||||||
# If you install passlib and bcrypt, the password system will use bcrypt by default, if not, will use native crypt libc
|
# If you install passlib and bcrypt, the password system will use bcrypt by default, if not, will use native crypt libc
|
||||||
|
|
||||||
setup(name='paramecio',
|
setup(name='paramecio',
|
||||||
version='1.5.0',
|
version='1.6.0',
|
||||||
description='Simple Web Framework based in bottlepy and Mako.',
|
description='Simple Web Framework based in bottlepy and Mako.',
|
||||||
long_description='This framework is a simple framework used for create web apps. Paramecio is modular and fast. By default have a module called admin that can be used for create admin sites',
|
long_description='This framework is a simple framework used for create web apps. Paramecio is modular and fast. By default have a module called admin that can be used for create admin sites',
|
||||||
author='Antonio de la Rosa Caballero',
|
author='Antonio de la Rosa Caballero',
|
||||||
author_email='antonio.delarosa@coesinfo.com',
|
author_email='antonio.delarosa@salirdelhoyo.com',
|
||||||
url='https://github.com/paramecio/parameciofm/',
|
url='https://git.cuchulu.com/paramecio/parameciofm/',
|
||||||
packages=['paramecio', 'paramecio.i18n', 'paramecio.settings'],
|
packages=['paramecio', 'paramecio.i18n', 'paramecio.settings'],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=['bottle', 'mako', 'pymysql', 'sqlalchemy',
|
install_requires=['bottle', 'mako', 'pymysql', 'sqlalchemy', 'oslo.concurrency', 'itsdangerous', 'colorama','cherrypy', 'arrow'],
|
||||||
'oslo.concurrency', 'itsdangerous', 'colorama','cherrypy', 'arrow'],
|
|
||||||
entry_points={'console_scripts': [
|
entry_points={'console_scripts': [
|
||||||
'paramecio = paramecio.console:start',
|
'paramecio = paramecio.console:start',
|
||||||
|
'parameciodb = paramecio.cromosoma.dbamin.start'
|
||||||
]},
|
]},
|
||||||
license='GPLV3',
|
license='AGPLV3',
|
||||||
platforms = 'any',
|
platforms = 'any',
|
||||||
classifiers=['Development Status :: 1 - Beta',
|
classifiers=['Development Status :: 1 - Beta',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'License :: OSI Approved :: GPLV2 License',
|
'License :: OSI Approved :: AGPLV3 License',
|
||||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
|
'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
|
||||||
'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
|
'Topic :: Internet :: WWW/HTTP :: HTTP Servers',
|
||||||
'Topic :: Internet :: WWW/HTTP :: WSGI',
|
'Topic :: Internet :: WWW/HTTP :: WSGI',
|
||||||
|
|
@ -38,9 +38,6 @@ setup(name='paramecio',
|
||||||
'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
|
'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
|
||||||
'Topic :: Software Development :: Libraries :: Application Frameworks',
|
'Topic :: Software Development :: Libraries :: Application Frameworks',
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
'Programming Language :: Python :: 3.3',
|
'Programming Language :: Python :: 3.8'
|
||||||
'Programming Language :: Python :: 3.4',
|
|
||||||
'Programming Language :: Python :: 3.5',
|
|
||||||
'Programming Language :: Python :: 3.6'
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue