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
|
||||
|
||||
"""
|
||||
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 paramecio.citoplasma.sessions import get_session
|
||||
import json
|
||||
from bottle import request
|
||||
import gettext
|
||||
import os
|
||||
|
||||
yes_session=False
|
||||
|
||||
i18n_module={}
|
||||
|
||||
def load_lang(*args):
|
||||
"""A function for load the lang module dinamically
|
||||
"""
|
||||
|
||||
for module in args:
|
||||
|
||||
|
|
@ -26,9 +49,57 @@ def load_lang(*args):
|
|||
|
||||
# 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 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'
|
||||
|
||||
dict_i18n=['en-US', 'es-ES']
|
||||
|
|
@ -42,6 +113,7 @@ class I18n:
|
|||
|
||||
@staticmethod
|
||||
def get_default_lang():
|
||||
"""Static method for get the default lang"""
|
||||
|
||||
lang=I18n.default_lang
|
||||
|
||||
|
|
@ -53,6 +125,15 @@ class I18n:
|
|||
|
||||
@staticmethod
|
||||
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:
|
||||
lang=I18n.get_default_lang()
|
||||
|
|
@ -67,6 +148,11 @@ class I18n:
|
|||
|
||||
@staticmethod
|
||||
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)
|
||||
|
||||
|
|
@ -93,3 +179,4 @@ class I18n:
|
|||
|
||||
return json.dumps(arr_final)
|
||||
|
||||
common_pgettext=PGetText(__file__)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ except:
|
|||
key_encrypt=create_key_encrypt_256(30)
|
||||
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
|
||||
import os
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ from settings import config
|
|||
from importlib import import_module
|
||||
|
||||
def start():
|
||||
"""Module 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)
|
||||
exit(1)
|
||||
|
||||
f=open('./settings/modules.py', 'w')
|
||||
|
||||
with open('./settings/modules.py', 'w') as f:
|
||||
f.write("".join(modules))
|
||||
|
||||
f.close()
|
||||
|
||||
if __name__=="__main__":
|
||||
start()
|
||||
|
|
|
|||
|
|
@ -1,22 +1,47 @@
|
|||
"""
|
||||
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 import coreforms
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
#from bs4 import BeautifulSoup
|
||||
import bleach
|
||||
|
||||
class IntegerField(PhangoField):
|
||||
|
||||
"""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):
|
||||
"""
|
||||
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)
|
||||
self.default_value=0
|
||||
|
||||
|
||||
self.type_sql='int({})'.format(self.size)
|
||||
|
||||
def check(self, value):
|
||||
|
||||
"""Method for check if value is integer
|
||||
|
|
@ -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):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
|
@ -85,6 +115,7 @@ class FloatField(PhangoField):
|
|||
|
||||
self.error_default="The value is zero"
|
||||
self.default_value=0
|
||||
self.type_sql='float'.format(self.size)
|
||||
|
||||
def check(self, value):
|
||||
|
||||
|
|
@ -123,26 +154,55 @@ class FloatField(PhangoField):
|
|||
return 'FLOAT NOT NULL DEFAULT "0"'
|
||||
|
||||
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):
|
||||
|
||||
return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"'
|
||||
|
||||
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):
|
||||
|
||||
return 'DOUBLE NOT NULL DEFAULT "0"'
|
||||
|
||||
class CharField(PhangoField):
|
||||
"""Simple alias for PhangoField"""
|
||||
|
||||
pass
|
||||
|
||||
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):
|
||||
"""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)
|
||||
|
||||
self.type_sql='text'
|
||||
|
||||
self.set_default='NOT NULL'
|
||||
|
||||
def get_type_sql(self):
|
||||
|
|
@ -153,19 +213,107 @@ class TextField(PhangoField):
|
|||
|
||||
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):
|
||||
"""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)
|
||||
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):
|
||||
"""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):
|
||||
"""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=[]):
|
||||
"""
|
||||
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)
|
||||
|
||||
|
|
@ -192,7 +340,7 @@ class ForeignKeyField(IntegerField):
|
|||
value=super().check(value)
|
||||
|
||||
if value=='0' or value==0:
|
||||
value='NULL'
|
||||
value=None
|
||||
|
||||
return value
|
||||
|
||||
|
|
@ -206,8 +354,15 @@ class ForeignKeyField(IntegerField):
|
|||
|
||||
|
||||
class BooleanField(IntegerField):
|
||||
"""Field for boolean values
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -219,6 +374,8 @@ class BooleanField(IntegerField):
|
|||
self.default_error="Need 0 or 1 value"
|
||||
self.default_value=0
|
||||
|
||||
self.type_sql='tinyint(1)'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
self.error=False
|
||||
|
|
@ -231,6 +388,7 @@ class BooleanField(IntegerField):
|
|||
if value<0 or value>1:
|
||||
self.txt_error=self.default_error
|
||||
self.error=True
|
||||
value=0
|
||||
|
||||
except:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,55 @@
|
|||
#!/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 html import escape
|
||||
|
||||
#Forms para python3
|
||||
|
||||
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):
|
||||
"""
|
||||
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.name=name
|
||||
|
|
@ -17,26 +59,45 @@ class BaseForm:
|
|||
self.field=None
|
||||
self.required=False
|
||||
self.txt_error=''
|
||||
self.error=False
|
||||
self.name_field_id=self.name+'_form'
|
||||
self.help=''
|
||||
self.placeholder=''
|
||||
|
||||
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):
|
||||
"""Method for show the value of form formatted
|
||||
|
||||
Args:
|
||||
value (mixed): The value of field form
|
||||
"""
|
||||
|
||||
return value
|
||||
|
||||
#Method for escape value for html input. DON'T CHANGE IF YOU DON'T KNOWN WHAT ARE YOU DOING
|
||||
|
||||
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)
|
||||
|
||||
return value.replace('"', '"').replace("'", ''')
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -45,6 +106,8 @@ class BaseForm:
|
|||
return ""
|
||||
|
||||
class SimpleTextForm(BaseForm):
|
||||
"""Form for simple text
|
||||
"""
|
||||
|
||||
def __init__(self, name, value):
|
||||
super().__init__(name, value)
|
||||
|
|
@ -56,6 +119,8 @@ class SimpleTextForm(BaseForm):
|
|||
return super().form()+' '+self.after_text
|
||||
|
||||
class TextForm(BaseForm):
|
||||
"""Form for simple text form
|
||||
"""
|
||||
|
||||
def __init__(self, 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>'
|
||||
|
||||
class PasswordForm(BaseForm):
|
||||
"""Form for password forms
|
||||
"""
|
||||
|
||||
def __init__(self, name, value, show_password=False):
|
||||
super(PasswordForm, self).__init__(name, value)
|
||||
|
|
@ -79,6 +146,8 @@ class PasswordForm(BaseForm):
|
|||
return value
|
||||
|
||||
class HiddenForm(BaseForm):
|
||||
"""Form for hidden forms
|
||||
"""
|
||||
|
||||
def __init__(self, name, value):
|
||||
super(HiddenForm, self).__init__(name, value)
|
||||
|
|
@ -86,8 +155,16 @@ class HiddenForm(BaseForm):
|
|||
|
||||
|
||||
class SelectForm(BaseForm):
|
||||
"""Form for select html form
|
||||
"""
|
||||
|
||||
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)
|
||||
self.arr_select=elements
|
||||
|
||||
|
|
@ -107,8 +184,19 @@ class SelectForm(BaseForm):
|
|||
return the_form
|
||||
|
||||
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):
|
||||
"""
|
||||
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)
|
||||
|
||||
try:
|
||||
|
|
@ -129,12 +217,16 @@ class SelectModelForm(SelectForm):
|
|||
|
||||
|
||||
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['']=''
|
||||
|
||||
with self.model.select([self.field_name, self.field_value], True) as 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])
|
||||
|
||||
try:
|
||||
|
|
@ -147,6 +239,10 @@ class SelectModelForm(SelectForm):
|
|||
return super().form()
|
||||
|
||||
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['']=''
|
||||
|
||||
|
|
@ -168,7 +264,8 @@ class SelectModelForm(SelectForm):
|
|||
|
||||
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)
|
||||
|
||||
|
|
@ -186,6 +283,8 @@ class SelectModelForm(SelectForm):
|
|||
|
||||
|
||||
def create_son(self, parent_id, arr_son, separator=''):
|
||||
"""Recursive method for generate parents and children dictionary
|
||||
"""
|
||||
|
||||
if parent_id in arr_son:
|
||||
for son in arr_son[parent_id]:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,25 @@
|
|||
#!/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 os,traceback
|
||||
import sys, inspect
|
||||
|
|
@ -10,11 +30,18 @@ from pathlib import Path
|
|||
from colorama import init, Fore, Back, Style
|
||||
from importlib import import_module, reload
|
||||
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():
|
||||
"""Function for create and update mysql tables using webmodel classes and fields how source.
|
||||
"""
|
||||
|
||||
connection=WebModel.connection()
|
||||
|
||||
|
|
@ -103,6 +130,32 @@ def start():
|
|||
|
||||
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
|
||||
#new_tables=set(tables)-set(table_exists)
|
||||
|
||||
|
|
@ -160,6 +213,23 @@ def start():
|
|||
print(Style.BRIGHT+"Checking old versions of model for find changes...")
|
||||
|
||||
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("")
|
||||
#Check if new table
|
||||
|
||||
|
|
@ -182,7 +252,8 @@ def start():
|
|||
|
||||
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)
|
||||
|
||||
|
|
@ -226,13 +297,15 @@ def start():
|
|||
|
||||
#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)
|
||||
|
||||
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)
|
||||
|
||||
|
|
@ -240,13 +313,15 @@ def start():
|
|||
|
||||
#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)
|
||||
|
||||
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)
|
||||
|
||||
|
|
@ -254,25 +329,39 @@ def start():
|
|||
|
||||
#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)
|
||||
|
||||
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':
|
||||
|
||||
if table in foreignkey_fields:
|
||||
|
||||
if f in foreignkey_fields[table]:
|
||||
|
||||
fields_to_delete_constraint.append(f)
|
||||
|
||||
changes+=1
|
||||
|
||||
for f, v in WebModel.model[old_table].fields.items():
|
||||
# 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:
|
||||
|
||||
#Add constraint
|
||||
|
||||
if v.foreignkey==True:
|
||||
#if v.foreignkey==True:
|
||||
|
||||
if table in foreignkey_fields:
|
||||
|
||||
if f in foreignkey_fields[table]:
|
||||
|
||||
fields_to_delete_constraint.append(f)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from paramecio.cromosoma.corefields import CharField
|
||||
import re
|
||||
|
||||
mail_pattern=re.compile("\w[\w\.-]*@\w[\w\.-]+\.\w+")
|
||||
mail_pattern=re.compile(r"\w[\w\.-]*@\w[\w\.-]+\.\w+")
|
||||
|
||||
class EmailField(CharField):
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.databases.sqlalchemy import SqlClass
|
||||
from paramecio.cromosoma.coreforms import BaseForm, HiddenForm
|
||||
import sys
|
||||
import re
|
||||
import uuid
|
||||
from importlib import import_module, reload
|
||||
from collections import OrderedDict
|
||||
from paramecio.cromosoma.databases.sqlalchemy import SqlClass
|
||||
from paramecio.cromosoma.coreforms import BaseForm, HiddenForm
|
||||
import copy
|
||||
import traceback
|
||||
|
||||
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):
|
||||
"""
|
||||
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
|
||||
|
||||
|
|
@ -50,7 +86,7 @@ class PhangoField:
|
|||
|
||||
self.txt_error=""
|
||||
|
||||
# Themodel where this component or field live
|
||||
# The model where this component or field live
|
||||
|
||||
self.model=None
|
||||
|
||||
|
|
@ -100,7 +136,7 @@ class PhangoField:
|
|||
|
||||
# Error by default
|
||||
|
||||
self.error_default='Error: field required'
|
||||
self.error_default='Error: '+self.name+' field required'
|
||||
|
||||
# Show this value formatted
|
||||
|
||||
|
|
@ -110,27 +146,28 @@ class PhangoField:
|
|||
|
||||
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):
|
||||
"""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+'"'
|
||||
|
||||
def show_formatted(self, value):
|
||||
"""Method for format the value to show in html or others text outputs"""
|
||||
|
||||
return value
|
||||
|
||||
# This method for check the value
|
||||
|
||||
|
||||
def check(self, value):
|
||||
"""Method for check if value is valid for this type field"""
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
value=str(value).strip()
|
||||
|
||||
#Minimal escape for prevent basic js injection.
|
||||
|
||||
if self.escape==False:
|
||||
value=value.replace('<', '<')
|
||||
|
||||
|
|
@ -150,6 +187,7 @@ class PhangoField:
|
|||
pass
|
||||
|
||||
def create_form(self):
|
||||
"""Create a BaseForm object for use in forms functions and methods"""
|
||||
#self.name, self.default_value,
|
||||
|
||||
final_parameters=copy.copy(self.extra_parameters)
|
||||
|
|
@ -165,6 +203,7 @@ class PhangoField:
|
|||
return form
|
||||
|
||||
def change_form(self, new_form, parameters):
|
||||
"""Change the base form of the field and its parameters"""
|
||||
|
||||
self.name_form=new_form
|
||||
|
||||
|
|
@ -174,6 +213,10 @@ class PhangoField:
|
|||
pass
|
||||
|
||||
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):
|
||||
super(PrimaryKeyField, self).__init__(name, size, required)
|
||||
|
|
@ -181,6 +224,7 @@ class PrimaryKeyField(PhangoField):
|
|||
self.name_form=HiddenForm
|
||||
self.required=False
|
||||
self.error_default="The value is zero"
|
||||
self.type_sql='int({})'.format(self.size)
|
||||
|
||||
def check(self, value):
|
||||
|
||||
|
|
@ -217,6 +261,24 @@ class PrimaryKeyField(PhangoField):
|
|||
|
||||
|
||||
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')
|
||||
|
||||
|
|
@ -248,12 +310,53 @@ class WebModel:
|
|||
|
||||
@staticmethod
|
||||
def connection():
|
||||
"""Static method for make a connection using SqlClass
|
||||
|
||||
Returns: Return a SqlClass connection for mysql db.
|
||||
"""
|
||||
|
||||
return SqlClass(WebModel.connections['default'])
|
||||
|
||||
# Init the class
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -277,7 +380,7 @@ class WebModel:
|
|||
|
||||
# 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
|
||||
|
||||
|
|
@ -357,12 +460,18 @@ class WebModel:
|
|||
|
||||
self.enctype=False
|
||||
|
||||
self.model_id=0
|
||||
|
||||
self.dummy=0
|
||||
|
||||
# A method for add the connection
|
||||
|
||||
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
|
||||
|
||||
# Reset conditions
|
||||
|
|
@ -378,6 +487,11 @@ class WebModel:
|
|||
# A method for change the name of table
|
||||
|
||||
def change_name(self, name):
|
||||
""" A method for change the name of table
|
||||
|
||||
Args;
|
||||
name (str): The new name of table
|
||||
"""
|
||||
|
||||
self.name=name
|
||||
|
||||
|
|
@ -386,6 +500,7 @@ class WebModel:
|
|||
# A method where create the new fields of this model
|
||||
|
||||
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(dir(self))
|
||||
|
|
@ -395,6 +510,14 @@ class WebModel:
|
|||
# A method for register the fields
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -436,6 +559,13 @@ class WebModel:
|
|||
# Method for make queries
|
||||
|
||||
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()
|
||||
return self.sqlclass.query(str_query, args, connection_id)
|
||||
|
|
@ -443,6 +573,8 @@ class WebModel:
|
|||
# Method for clean fields
|
||||
|
||||
def clean_fields(self):
|
||||
"""Method for delete fields from self.fields dict"""
|
||||
|
||||
clean=self.fields_to_clean
|
||||
for field in self.fields_to_clean:
|
||||
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.
|
||||
|
||||
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()
|
||||
|
||||
|
|
@ -466,7 +606,6 @@ class WebModel:
|
|||
|
||||
try:
|
||||
|
||||
#fields, values, update_values=self.check_all_fields(dict_values, external_agent)
|
||||
arr_return=self.check_all_fields(dict_values, external_agent)
|
||||
|
||||
if arr_return:
|
||||
|
|
@ -476,11 +615,10 @@ class WebModel:
|
|||
else:
|
||||
return False
|
||||
|
||||
except:
|
||||
self.query_error='Cannot insert the new row'
|
||||
print(sys.exc_info()[0])
|
||||
raise
|
||||
#return False
|
||||
except Exception as e:
|
||||
self.query_error='Cannot insert the new row '+str(e)
|
||||
#print(sys.exc_info()[0])
|
||||
return False
|
||||
|
||||
c=len(values)
|
||||
|
||||
|
|
@ -509,6 +647,15 @@ class WebModel:
|
|||
# Update method. For update one or many rows.
|
||||
|
||||
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()
|
||||
|
||||
|
|
@ -583,7 +730,7 @@ class WebModel:
|
|||
"""
|
||||
|
||||
def reset_conditions(self):
|
||||
|
||||
"""Method for reset self.conditions to default values"""
|
||||
self.conditions=["WHERE 1=1", []]
|
||||
self.limit=''
|
||||
|
||||
|
|
@ -591,6 +738,19 @@ class WebModel:
|
|||
#Type assoc can be assoc for return dictionaries
|
||||
|
||||
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()
|
||||
|
||||
|
|
@ -706,14 +866,29 @@ class WebModel:
|
|||
# Show results in a dictionary
|
||||
|
||||
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()
|
||||
|
||||
def insert_id(self):
|
||||
"""Method for get the id from last row inserted in table"""
|
||||
|
||||
return self.last_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]]
|
||||
|
||||
|
|
@ -731,6 +906,15 @@ class WebModel:
|
|||
pass
|
||||
|
||||
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]]
|
||||
|
||||
|
|
@ -752,6 +936,15 @@ class WebModel:
|
|||
return row
|
||||
|
||||
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"
|
||||
|
||||
|
|
@ -770,6 +963,16 @@ class WebModel:
|
|||
|
||||
|
||||
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:
|
||||
fields_selected=list(self.fields.keys())
|
||||
|
|
@ -810,6 +1013,16 @@ class WebModel:
|
|||
|
||||
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:
|
||||
def check_index(index):
|
||||
return index
|
||||
|
|
@ -855,6 +1068,15 @@ class WebModel:
|
|||
# A method por count num rows affected for sql conditions
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -901,12 +1123,19 @@ class WebModel:
|
|||
# A method for delete rows using sql conditions
|
||||
|
||||
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()
|
||||
|
||||
#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)
|
||||
|
||||
|
|
@ -923,6 +1152,15 @@ class WebModel:
|
|||
return False
|
||||
|
||||
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]
|
||||
|
||||
|
|
@ -930,6 +1168,15 @@ class WebModel:
|
|||
|
||||
@staticmethod
|
||||
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)):
|
||||
try:
|
||||
|
|
@ -939,6 +1186,15 @@ class WebModel:
|
|||
return '('+', '.join(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)):
|
||||
in_list[x]=str(self.fields[field].check(in_list[x]))
|
||||
|
|
@ -946,6 +1202,15 @@ class WebModel:
|
|||
return '("'+'", "'.join(in_list)+'")'
|
||||
|
||||
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.append('ASC')
|
||||
|
|
@ -964,6 +1229,15 @@ class WebModel:
|
|||
return self
|
||||
|
||||
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])
|
||||
|
||||
|
|
@ -979,6 +1253,7 @@ class WebModel:
|
|||
# Method for create sql tables
|
||||
|
||||
def create_table(self):
|
||||
"""Method for create a table from this model object"""
|
||||
|
||||
#self.connect_to_db()
|
||||
|
||||
|
|
@ -1089,6 +1364,12 @@ class WebModel:
|
|||
# Method for drop sql tables and related
|
||||
|
||||
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 an array with all fields
|
||||
|
|
@ -1099,6 +1380,20 @@ class WebModel:
|
|||
#Check of all fields in table.
|
||||
|
||||
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=[]
|
||||
values=[]
|
||||
|
|
@ -1227,6 +1522,7 @@ class WebModel:
|
|||
#Reset the require field in fields
|
||||
|
||||
def reset_require(self):
|
||||
"""Reset the require attribute in fields"""
|
||||
|
||||
for k, v in self.fields.items():
|
||||
|
||||
|
|
@ -1237,6 +1533,7 @@ class WebModel:
|
|||
#Reload the require field in fields
|
||||
|
||||
def reload_require(self):
|
||||
"""Reload the require field in fields"""
|
||||
|
||||
for k,r in self.fields.items():
|
||||
self.fields[k].required=r
|
||||
|
|
@ -1244,6 +1541,7 @@ class WebModel:
|
|||
#Choose all fields to updated
|
||||
|
||||
def set_valid_fields(self, fields={}):
|
||||
"""Choose all fields to updated"""
|
||||
|
||||
if len(fields)==0:
|
||||
fields=self.fields.keys()
|
||||
|
|
@ -1253,6 +1551,7 @@ class WebModel:
|
|||
#Create a form based in table.
|
||||
|
||||
def create_forms(self, arr_fields=[]):
|
||||
"""Create a form based in table."""
|
||||
|
||||
self.forms=OrderedDict()
|
||||
|
||||
|
|
@ -1270,6 +1569,12 @@ class WebModel:
|
|||
return arr_fields
|
||||
|
||||
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()
|
||||
|
||||
|
|
@ -1281,6 +1586,11 @@ class WebModel:
|
|||
self.forms=new_dict
|
||||
|
||||
def show_errors(self):
|
||||
"""Get all errors of model last operation.
|
||||
|
||||
Returns:
|
||||
error_txt (str): A string with all errors.
|
||||
"""
|
||||
|
||||
arr_error=[]
|
||||
error_txt=''
|
||||
|
|
@ -1301,6 +1611,11 @@ class WebModel:
|
|||
return error_txt
|
||||
|
||||
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= {}
|
||||
error_txt=''
|
||||
|
|
@ -1318,12 +1633,15 @@ class WebModel:
|
|||
return arr_error
|
||||
|
||||
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.reset_require()
|
||||
|
||||
|
||||
def close(self):
|
||||
"""Method for close sqlclass db connection"""
|
||||
|
||||
self.sqlclass.close()
|
||||
|
||||
|
|
@ -1341,6 +1659,7 @@ class WebModel:
|
|||
#del sqlclass.connection[key]
|
||||
@staticmethod
|
||||
def escape_sql(value):
|
||||
"""Manual escape for sql, you shouldn't use it"""
|
||||
|
||||
value=str(value)
|
||||
|
||||
|
|
@ -1354,6 +1673,11 @@ class WebModel:
|
|||
# Set post values from a post array
|
||||
|
||||
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():
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from settings import config
|
|||
from mimetypes import guess_type
|
||||
from paramecio.cromosoma.webmodel import WebModel
|
||||
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.wsgiapp import app
|
||||
#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.urls import make_url, add_get_parameters, redirect
|
||||
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_admin
|
||||
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"
|
||||
|
||||
#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
|
||||
|
||||
|
|
@ -135,11 +136,3 @@ yes_static=True
|
|||
#Database mysql config, if you want anything...
|
||||
|
||||
#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
|
||||
|
||||
app=bottle.app()
|
||||
|
|
|
|||
23
setup.py
23
setup.py
|
|
@ -5,32 +5,32 @@ import os
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
raise NotImplementedError("Sorry, you need at least Python 3.3 for use paramecio.")
|
||||
if sys.version_info < (3, 8):
|
||||
raise NotImplementedError("Sorry, you need at least Python 3.8 for use paramecio.")
|
||||
|
||||
#import paramecio
|
||||
# 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
|
||||
|
||||
setup(name='paramecio',
|
||||
version='1.5.0',
|
||||
version='1.6.0',
|
||||
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',
|
||||
author='Antonio de la Rosa Caballero',
|
||||
author_email='antonio.delarosa@coesinfo.com',
|
||||
url='https://github.com/paramecio/parameciofm/',
|
||||
author_email='antonio.delarosa@salirdelhoyo.com',
|
||||
url='https://git.cuchulu.com/paramecio/parameciofm/',
|
||||
packages=['paramecio', 'paramecio.i18n', 'paramecio.settings'],
|
||||
include_package_data=True,
|
||||
install_requires=['bottle', 'mako', 'pymysql', 'sqlalchemy',
|
||||
'oslo.concurrency', 'itsdangerous', 'colorama','cherrypy', 'arrow'],
|
||||
install_requires=['bottle', 'mako', 'pymysql', 'sqlalchemy', 'oslo.concurrency', 'itsdangerous', 'colorama','cherrypy', 'arrow'],
|
||||
entry_points={'console_scripts': [
|
||||
'paramecio = paramecio.console:start',
|
||||
'parameciodb = paramecio.cromosoma.dbamin.start'
|
||||
]},
|
||||
license='GPLV3',
|
||||
license='AGPLV3',
|
||||
platforms = 'any',
|
||||
classifiers=['Development Status :: 1 - Beta',
|
||||
'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 :: HTTP Servers',
|
||||
'Topic :: Internet :: WWW/HTTP :: WSGI',
|
||||
|
|
@ -38,9 +38,6 @@ setup(name='paramecio',
|
|||
'Topic :: Internet :: WWW/HTTP :: WSGI :: Server',
|
||||
'Topic :: Software Development :: Libraries :: Application Frameworks',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6'
|
||||
'Programming Language :: Python :: 3.8'
|
||||
],
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue