ผลต่างระหว่างรุ่นของ "Quick Start for Developers"
ไม่มีความย่อการแก้ไข |
|||
| (ไม่แสดง 55 รุ่นระหว่างกลางโดยผู้ใช้คนเดียวกัน) | |||
| บรรทัดที่ 2: | บรรทัดที่ 2: | ||
== สร้าง Table - Database == | == สร้าง Table - Database == | ||
เป็นหน้าที่มี table อย่างเดียวดังในภาพ | |||
< | <image1><p> | ||
* สิ่งของที่จำเป็นในการสร้างมี | |||
** สร้างดาต้าเบส: จะถูกเขียนอยู่ใน model.py | |||
** สร้างองค์ประกอบตัวตาราง(ตารางต้องมีรูปร่างหน้าตาอย่างไร ต้องโชว์คอลัมน์อะไรบ้าง): จะถูกเขียนอยู่ใน table.py | |||
** สร้างหน้าHTML: จะถูกเขียนอยู่ใน html.py | |||
** สร้างท่องที่เชื่อมระหว่าง UX-UI เราจะเรียกท่อนนี้ว่า route: จะถูกเขียนอยู่ใน routes.py | |||
โดยไฟล์ที่จำเป็นมีดังนี้ | โดยไฟล์ที่จำเป็นมีดังนี้ | ||
=== | === model.py === | ||
model.py จะเป็นตัวกำหนดโครงสร้างดาต้าเบส โดยชื่อคาสจะเอาไปตั้งเป็นชื่อตารางดาต้าเบส และตัวแปรแต่ละตัวเปรียบเสมือนfield ที่จะต้องกำหนดชนิดตัวแปรเข้าไป (สามารถไปดูวิธีการสร้างModelแบบละเอียดได้ที่ [[CuneiModel]]) | |||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
class Anime(CuneiModel): | class Anime(CuneiModel): | ||
| บรรทัดที่ 31: | บรรทัดที่ 39: | ||
** '''{{code|lang=python|'DateField()'}}''': ? | ** '''{{code|lang=python|'DateField()'}}''': ? | ||
=== | === table.py === | ||
ออกแบบรูปร่างหน้าตาตัวตาราง เพื่อที่จะเอาข้อมูลที่สร้างไว้ในดาตาเบสมาลง ทั้งนี้สามารถกำหนดความกว้างคอลัมน์เป็น0ได้ หากไม่ต้องการให้ผู้ใช้เห็น และสามารถกำหนดคอลัมน์เพิ่มเติมจากAttributeที่มีอยู่ในดาต้าเบสได้ (Attributeที่มีอยู่ในดาต้าเบสต้องนำมาสร้างใส่คอลัมน์ให้หมด และสามารถสร้างคอลัมน์ในตารางเพิ่มนอกเหนือจากAttributeที่มีอยู่ในดาตาเบสได้) | |||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
class Anime_table(CuneiTable): | class Anime_table(CuneiTable): | ||
| บรรทัดที่ 62: | บรรทัดที่ 71: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== ฟังก์ชั่นการใช้งานและตัวแปร ==== | ==== ฟังก์ชั่นการใช้งานและตัวแปร ==== | ||
*คำสั่งในการใช้งาน | *คำสั่งในการใช้งาน สามารถเข้าไปดูวิธีสร้างตารางแบบละเอียดได้ที่ [[CuneiTable#Define]] | ||
** '''{{code|lang=python|'MASTER_colsizes'}}''': ความกว้างของช่องข้อมมูล(ทุกคอลัมน์ที่อยู่ในdatabaseต้องมีช่องข้อมูลเป็นของตัวเองทั้งหมด แต่สามารถใส่ค่าเป็น 0 ได้ หากไม่อยากให้ผู้ใช้เห็น) | ** '''{{code|lang=python|'MASTER_colsizes'}}''': ความกว้างของช่องข้อมมูล(ทุกคอลัมน์ที่อยู่ในdatabaseต้องมีช่องข้อมูลเป็นของตัวเองทั้งหมด แต่สามารถใส่ค่าเป็น 0 ได้ หากไม่อยากให้ผู้ใช้เห็น) | ||
** '''{{code|lang=python|'MASTER_colnames'}}''': หัวคอลัมน์ที่ผู้ใช้เห็น(ไม่จำเป็นต้องตรงกับที่อยู่ในdatabase) | ** '''{{code|lang=python|'MASTER_colnames'}}''': หัวคอลัมน์ที่ผู้ใช้เห็น(ไม่จำเป็นต้องตรงกับที่อยู่ในdatabase) | ||
| บรรทัดที่ 71: | บรรทัดที่ 80: | ||
** '''{{code|lang=python|'lazy_gettext'}}''': แปรช่องนี้ให้เป็นภาษาที่มนุยษ์เข้าใจ | ** '''{{code|lang=python|'lazy_gettext'}}''': แปรช่องนี้ให้เป็นภาษาที่มนุยษ์เข้าใจ | ||
** '''{{code|lang=python|'class SubForm'}}''': เป็นเรื่องของ Form กรุณาไปดูที่ [[CuneiForm#Define]] | ** '''{{code|lang=python|'class SubForm'}}''': เป็นเรื่องของ Form กรุณาไปดูที่ [[CuneiForm#Define]] | ||
=== routes.py === | |||
=== | === html.py === | ||
มีส่วนประกอบที่สำคัญดังนี้ | มีส่วนประกอบที่สำคัญดังนี้ | ||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
| บรรทัดที่ 155: | บรรทัดที่ 165: | ||
** '''{{code|lang=python|'prefix'}}''': ชื่อที่ใช้ตั้งเป็นIDของTable (ต้องตั้งชื่อไฟล์HTMLเป็นชื่อนี้ด้วย) | ** '''{{code|lang=python|'prefix'}}''': ชื่อที่ใช้ตั้งเป็นIDของTable (ต้องตั้งชื่อไฟล์HTMLเป็นชื่อนี้ด้วย) | ||
** '''{{code|lang=python|'perm_bit'}}''': สิทธิ์ในการเข้าถึงหน้านี้ | ** '''{{code|lang=python|'perm_bit'}}''': สิทธิ์ในการเข้าถึงหน้านี้ | ||
**:- 4 สามารถ | **:- 4 สามารถ เพิ่ม/แก้ไข/ลบ | ||
**:- 3 สามารถ | **:- 3 สามารถ เพิ่ม/แก้ไข | ||
**:- 2 สามารถ | **:- 2 สามารถ เพิ่ม | ||
**:- 1 สามารถ | **:- 1 สามารถ ดูได้เพียงอย่างเดียว | ||
** '''{{code|lang=python|'editable'}}''': สถานะปุ่มแก้ไขใน Table Anime จะทำการdimหรือไม่ | |||
** '''{{code|lang=python|'editable'}}''': | |||
** '''{{code|lang=python|'populate_route'}}''': เปิดให้มีการrefetchหน้าใหม่ | ** '''{{code|lang=python|'populate_route'}}''': เปิดให้มีการrefetchหน้าใหม่ | ||
**:- | **:- argument ที่1: แสดงถึงหน้าที่ต้องการให้มีการrefetch | ||
**:- | **:- argument ที่2: เป็นคำสั่งยืนยันการrefetch | ||
** '''{{code|lang=python|'post_route'}}''': เรียกใช้mode Submit บันทึกลงdatabase | ** '''{{code|lang=python|'post_route'}}''': เรียกใช้mode Submit บันทึกลงdatabase | ||
**:- | **:- argument ที่1: แสดงถึงหน้าที่ต้องการให้มีการบันทึก | ||
**:- | **:- argument ที่2: เป็นคำสั่งยืนยันการบันทึก | ||
** '''{{code|lang=python|'in_modal'}}''': | ** '''{{code|lang=python|'searchable'}}''': สถานะปุ่มค้นหาใน Table Publisher จะทำการdimหรือไม่ | ||
** '''{{code|lang=python|'suppress_copy'}}''': สถานะปุ่มคัดลอกใน Table Publisher จะทำการdimหรือไม่ | |||
** '''{{code|lang=python|'in_modal'}}''': Tableนี้ใช้โครงสร้าง database ตัวไหน | |||
** '''{{code|lang=python|'modal_head'}}''': ชื่อที่จะอยู่บนหัวpopup | ** '''{{code|lang=python|'modal_head'}}''': ชื่อที่จะอยู่บนหัวpopup | ||
** '''{{code|lang=python|'table.form'}}''': เป็น Obj form ที่เก็บ field ทั้งหมดในหน้านี้ไว้ และใน | ** '''{{code|lang=python|'request.args.get'}}''': ถ้าเป็นโหมด '''{{code|lang=python|'fetch'}}''' (มีการเคลื่อนไหวของข้อมูล แต่ยังไม่ได้เข้าdata base)จะมีอยู่ 3 กรณี | ||
**:1. "qsch" กรณีนำเข้าข้อมูลจากการค้นหา (ครั้งแรกที่มีการเปิดpageขึ้นมา จะต้องเรียกข้อมูลทั้งหมดมาใส่ในtable) | |||
**:2. "ncol" กรณีเพิ่มข้อมูลขึ้นบันทักใหม่ | |||
**:3. "sel" กรณีเลือกข้อมูล | |||
** '''{{code|lang=python|'request.args.get'}}''':ถ้าเป็นโหมด '''{{code|lang=python|'is_submit'}}'''(มีข้อมูลที่จะต้องเข้าdata base หรือมีกิจกรรมที่ต้องยุ่งกับdata base)สามารถเปิด data base แล้วดำเนินการได้เลย | |||
** '''{{code|lang=python|'table.form'}}''': เป็น Obj form ที่เก็บ field ทั้งหมดในหน้านี้ไว้ และใน แต่ละfield ก็จะเก็บ dataของตัวเองไว้ | |||
** '''{{code|lang=python|'getattr(x,y).data'}}''': ดึงข้อมูลจาก Obj มาใช้ | ** '''{{code|lang=python|'getattr(x,y).data'}}''': ดึงข้อมูลจาก Obj มาใช้ | ||
** '''{{code|lang=python|'setattr'}}''': วิธีบันทึกของลงในObj | ** '''{{code|lang=python|'setattr'}}''': วิธีบันทึกของลงในObj | ||
** '''{{code|lang=python|'table.cnames'}}''': | ** '''{{code|lang=python|'table.cnames'}}''': เก็บชื่อคอลัมน์ที่มีทั้งหมดในdatabase + submit(สถานะการบันทึก) + del_submit(เป็นปุ่มที่เมื่อกดมันจะไปบังคับติ๊กถูกที่is_delอีกที)และ is_del(สภานะการลบ) | ||
*ขยายcode | *ขยายcode | ||
**<syntaxhighlight lang="python" line="1">publisher_tb = ...</syntaxhighlight> | **<syntaxhighlight lang="python" line="1">publisher_tb = ...</syntaxhighlight>เก็บ Obj Table ที่จะแสดงบนหน้าจอ สามารถเพิ่ม Obj Table ได้มากกว่า 1 Table | ||
**<syntaxhighlight lang="python" line="1">request.arge.get()</syntaxhighlight>sectionทำงานอยู่ 2 แบบคือ | **<syntaxhighlight lang="python" line="1">request.arge.get()</syntaxhighlight>sectionทำงานอยู่ 2 แบบคือ | ||
**:- fetch : จะทำงานทุกครั้งที่มีการreload หน้า(ทุกการreloadจะมีการดึงข้อมูลใหม่ทุกครั้ง) | **:- fetch : จะทำงานทุกครั้งที่มีการreload หน้า(ทุกการreloadจะมีการดึงข้อมูลใหม่ทุกครั้ง) | ||
| บรรทัดที่ 194: | บรรทัดที่ 210: | ||
@cunei_pos.route("/anime", methods=["GET", "POST"]) | @cunei_pos.route("/anime", methods=["GET", "POST"]) | ||
def anime(): | def anime(): | ||
table = Anime_Table(prefix=" | table = Anime_Table(prefix="anime_table", suppress_copy=False, editable=True) | ||
table.sch_tbs = [Publisher_Table(prefix="PublisherTable", perm_bit=4, editable=True, | table.sch_tbs = [Publisher_Table(prefix="PublisherTable", perm_bit=4, editable=True, | ||
populate_route=url_for("cunei_pos.publisher", fetch="yes"), | populate_route=url_for("cunei_pos.publisher", fetch="yes"), | ||
| บรรทัดที่ 206: | บรรทัดที่ 222: | ||
return single_tb_page(dbtype, tbname, model, module, table, new_default, fetch_all, request, forced_perm=4, in_modal="ModalPublisher", modal_head=lazy_gettext("Choose Publisher"))] | return single_tb_page(dbtype, tbname, model, module, table, new_default, fetch_all, request, forced_perm=4, in_modal="ModalPublisher", modal_head=lazy_gettext("Choose Publisher"))] | ||
</syntaxhighlight> | </syntaxhighlight> | ||
วิธีนี้จะช่วยทำให้โค้ดดูสั้นขึ้นและอ่านง่ายขึ้น โดยกำหนดแค่ค่าเริ่มต้น และรวบรวมตัวแปรให้ไปsetไว้ในที่เดียวกัน (ขั้นตอนในการดำเนินการเหมือนกัน เพียงแค่ย่อมันให้สั้นลงเท่านั้น) | |||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
get_default_anime | |||
</syntaxhighlight> | </syntaxhighlight> | ||
ฟังก์ชั่นนี้ จะทำหน้าที่จัดการค่าเริ่มต้นต่างๆที่ระบบควรมี | |||
<syntaxhighlight lang="python" line="1"> | |||
anime_grab_all | |||
</syntaxhighlight> | |||
ฟังก์ชั่นนี้ จะทำหน้าที่ดึงข้อมูลมีระบบควรจะรู้ออกมา | |||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
new_default = get_default_anime | new_default = get_default_anime | ||
</syntaxhighlight> | </syntaxhighlight> | ||
เป็นfunction Obj ที่จะคืนผล default กลับมา | เป็นfunction Obj ที่จะคืนผล default กลับมา โดยปกติแล้วจะเขียนfunctionนี้ในไฟล์ functions.py | ||
::<syntaxhighlight lang="python" line="1"> | ::<syntaxhighlight lang="python" line="1"> | ||
def get_default_anime(): | def get_default_anime(): | ||
return dict(stid=get_next_stid("Anime", Anime), id=-1) | return dict(stid=get_next_stid("Anime", Anime), id=-1) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
| บรรทัดที่ 240: | บรรทัดที่ 260: | ||
== สร้าง Form - non Database == | == สร้าง Form - non Database == | ||
เป็นหน้าที่แสดง Form อย่างเดียว โดยไม่มีการบันทึกของลงdatabase | เป็นหน้าที่แสดง Form อย่างเดียว โดยไม่มีการบันทึกของลงdatabase | ||
<image2> | <image2><p> | ||
* สิ่งของที่จำเป็นในการสร้างมี | |||
=== | ** สร้างดาต้าเบส: จะถูกเขียนอยู่ใน model.py | ||
** สร้างองค์ประกอบตัวฟอร์ม(ฟอร์มต้องมีรูปร่างหน้าตาอย่างไร ต้องโชว์คอลัมน์อะไรบ้าง): จะถูกเขียนอยู่ใน form.py | |||
** สร้างหน้าHTML: จะถูกเขียนอยู่ใน html.py | |||
** สร้างท่องที่เชื่อมระหว่าง UX-UI เราจะเรียกท่อนนี้ว่า route จะถูกเขียนอยู่ใน routes.py | |||
** ถ้าFormมีการคิดวิเคราะห์ที่ลึกมากขึ้น ไม่สามารถเขียนใน routes.py ได้ ให้ไปเขียนไว้ใน functions.py แทน | |||
โดยไฟล์ที่จำเป็นมีดังนี้ | |||
=== model.py === | |||
model.py จะเป็นตัวกำหนดโครงสร้างดาต้าเบส โดยชื่อคาสจะเอาไปตั้งเป็นชื่อตารางดาต้าเบส และตัวแปรแต่ละตัวเปรียบเสมือนfield ที่จะต้องกำหนดชนิดตัวแปรเข้าไป (สามารถไปดูวิธีการสร้างModelแบบละเอียดได้ที่[[cunei#...]]) | |||
<syntaxhighlight lang="python" line="1"> | |||
class Foreigner(CuneiModel): | |||
id = AutoIncrementField() | |||
passport = CharField() | |||
first_name = CharField(default="") | |||
middle_name = CharField(default="", null=True) | |||
last_name = CharField(default="", null=True) | |||
birthday = CharField() | |||
saving = FloatField() | |||
gender = CharField() | |||
status = CharField() | |||
spouse_first_name = CharField(default="", null=True) | |||
spouse_middle_name = CharField(default="", null=True) | |||
spouse_last_name = CharField(default="", null=True) | |||
address = TextField(default="", null=True) | |||
pict = CharField(null=True) | |||
class Meta: | |||
database = SqliteExtDatabase(None) | |||
</syntaxhighlight> | |||
==== ฟังก์ชั่นการใช้งานและชนิดช้อมูล ==== | |||
*คำสั่งในการใช้งาน | |||
** '''{{code|lang=python|'AutoIncrementField()'}}''': กำหนดให้มีรันตัวเลขอัตโนมัติ | |||
** '''{{code|lang=python|'null'}}''': กำหนดให้databaseสามารถเว้นว่างได้ | |||
** '''{{code|lang=python|'SqliteExtDatabase'}}''': เปิดdatabase | |||
*กำหนดชนิดของข้อมูล | |||
** '''{{code|lang=python|'IntegerField()'}}''': ตัวแปรชนิดตัวเลขที่เป็นจำนวนเต็ม | |||
** '''{{code|lang=python|'FloatField()'}}''': ตัวแปรชนิดตัวเลขที่จุดทศนิยม | |||
** '''{{code|lang=python|'CharField()'}}''': ตัวแปรชนิดที่เป็นตัวอักษร | |||
** '''{{code|lang=python|'TextField()'}}''': ตัวแปรชนิดที่เป็นตัวอักษรและช่องข้อความขยายได้ | |||
** '''{{code|lang=python|'DateField()'}}''': ? | |||
=== form.py === | |||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
class Foreigner_Form(FlaskForm, CuneiForm): | class Foreigner_Form(FlaskForm, CuneiForm): | ||
| บรรทัดที่ 334: | บรรทัดที่ 396: | ||
****acc: ตัวเลขที่ไว้ลงบัญชี | ****acc: ตัวเลขที่ไว้ลงบัญชี | ||
****pct: จำนวนที่เป็นพวกร้อนละ/เปอร์เซ็น | ****pct: จำนวนที่เป็นพวกร้อนละ/เปอร์เซ็น | ||
****qty: ตัวเลขที่เกี่ยวกับจำนวน/หน่วย | ****qty: ตัวเลขที่เกี่ยวกับจำนวน/หน่วย | ||
สามารถไปอ่านเพิ่มเติมได้ที่ [[CuneiForm#Available_Types]] | **:สามารถไปอ่านเพิ่มเติมได้ที่ [[CuneiForm#Available_Types]] | ||
**<syntaxhighlight lang="python" line="1"> | **<syntaxhighlight lang="python" line="1"> | ||
def validate_spouse_first_name(...) | |||
def validate_saving(...) | |||
def validate_birthday(...) | |||
</syntaxhighlight> เป็น function สร้างเงื่อนไขให้กับ field เช่น วันที่ที่กรอกต้องไม่น้อยกว่าหรือมากกว่าเท่านี้นะ โดยวิธีการตั้งชื่อให้ใส่ validate ไว้ข้างหน้าแล้วตามชื่อ _ชื่อ field ที่ต้องการสร้างเงื่อนไขให้ แล้วส่ง argument selfและชื่อfieldมาด้วย | </syntaxhighlight> เป็น function สร้างเงื่อนไขให้กับ field เช่น วันที่ที่กรอกต้องไม่น้อยกว่าหรือมากกว่าเท่านี้นะ โดยวิธีการตั้งชื่อให้ใส่ validate ไว้ข้างหน้าแล้วตามชื่อ _ชื่อ field ที่ต้องการสร้างเงื่อนไขให้ แล้วส่ง argument selfและชื่อfieldมาด้วย | ||
=== | === html.py === | ||
<syntaxhighlight lang=" | <syntaxhighlight lang="html" line="1"> | ||
{% import "cunei_forms.html" as CuneiForms %} | {% import "cunei_forms.html" as CuneiForms %} | ||
{% import "cunei_tables.html" as CuneiTables with context %} | {% import "cunei_tables.html" as CuneiTables with context %} | ||
| บรรทัดที่ 426: | บรรทัดที่ 488: | ||
{% block greeter_jvs %} | {% block greeter_jvs %} | ||
{% endblock greeter_jvs %} | {% endblock greeter_jvs %} | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== | * Tag ต่างๆที่ควรรู้ | ||
:- '''general_form_head / general_form_tail''' : Tagเปิด/ปิดส่วนของform ช่องข้อมูลทั้งหมดที่แสดงจะต้องอยู่ระหว่าง 2 บรรทัดนี้ | |||
:- '''<img id="form.pict.id">''' : หากข้อมูลที่ใส่เป็นข้อมูลประเภทรูปภาพ ให้ใช้Tagนี้ | |||
:- '''render_check''' : เป็น Tag ที่สร้างcheck box | |||
:- '''render_upload''' : เป็น Tag ที่สร้างแถบอัพโหลดไฟล์ | |||
:- '''render_blank''' : เป็น Tag ที่สร้างช่อง label และหัวของlabel | |||
:- '''render_radio''' : เป็น Tag ที่สร้าง Text redio | |||
:- '''render_submit''' : เป็น Tag ที่สร้างปุ่มsubmit ทุกครั้งที่สร้างฟอร์มจำเป็นต้องมี | |||
:- '''block greeter_jvs / endblock greeter_jvs''' : เป็น Tag ที่ไว้สำหรับเขียน javascript | |||
=== routes.py === | |||
<syntaxhighlight lang="python" line="1"> | <syntaxhighlight lang="python" line="1"> | ||
@cunei_pos.route("/foreigner", methods=["GET", "POST"]) | @cunei_pos.route("/foreigner", methods=["GET", "POST"]) | ||
รุ่นแก้ไขปัจจุบันเมื่อ 17:01, 23 พฤษภาคม 2568
Start here
สร้าง Table - Database
เป็นหน้าที่มี table อย่างเดียวดังในภาพ
<image1>
- สิ่งของที่จำเป็นในการสร้างมี
- สร้างดาต้าเบส: จะถูกเขียนอยู่ใน model.py
- สร้างองค์ประกอบตัวตาราง(ตารางต้องมีรูปร่างหน้าตาอย่างไร ต้องโชว์คอลัมน์อะไรบ้าง): จะถูกเขียนอยู่ใน table.py
- สร้างหน้าHTML: จะถูกเขียนอยู่ใน html.py
- สร้างท่องที่เชื่อมระหว่าง UX-UI เราจะเรียกท่อนนี้ว่า route: จะถูกเขียนอยู่ใน routes.py
โดยไฟล์ที่จำเป็นมีดังนี้
model.py
model.py จะเป็นตัวกำหนดโครงสร้างดาต้าเบส โดยชื่อคาสจะเอาไปตั้งเป็นชื่อตารางดาต้าเบส และตัวแปรแต่ละตัวเปรียบเสมือนfield ที่จะต้องกำหนดชนิดตัวแปรเข้าไป (สามารถไปดูวิธีการสร้างModelแบบละเอียดได้ที่ CuneiModel)
class Anime(CuneiModel):
id = AutoIncrementField()
stid = IntegerField()
code = CharField(default="")
name = CharField(default="")
type = CharField(default="")
Publisher = CharField(default="")
writer = CharField(default="", null=True)
class Meta:
database = SqliteExtDatabase(None)
ฟังก์ชั่นการใช้งานและชนิดช้อมูล
- คำสั่งในการใช้งาน
'AutoIncrementField()': กำหนดให้มีรันตัวเลขอัตโนมัติ'null': กำหนดให้databaseสามารถเว้นว่างได้'SqliteExtDatabase': เปิดdatabase
- กำหนดชนิดของข้อมูล
'IntegerField()': ตัวแปรชนิดตัวเลขที่เป็นจำนวนเต็ม'FloatField()': ตัวแปรชนิดตัวเลขที่จุดทศนิยม'CharField()': ตัวแปรชนิดที่เป็นตัวอักษร'TextField()': ตัวแปรชนิดที่เป็นตัวอักษรและช่องข้อความขยายได้'DateField()': ?
table.py
ออกแบบรูปร่างหน้าตาตัวตาราง เพื่อที่จะเอาข้อมูลที่สร้างไว้ในดาตาเบสมาลง ทั้งนี้สามารถกำหนดความกว้างคอลัมน์เป็น0ได้ หากไม่ต้องการให้ผู้ใช้เห็น และสามารถกำหนดคอลัมน์เพิ่มเติมจากAttributeที่มีอยู่ในดาต้าเบสได้ (Attributeที่มีอยู่ในดาต้าเบสต้องนำมาสร้างใส่คอลัมน์ให้หมด และสามารถสร้างคอลัมน์ในตารางเพิ่มนอกเหนือจากAttributeที่มีอยู่ในดาตาเบสได้)
class Anime_table(CuneiTable):
MASTER_colsizes = [1,3,10,10,0,7,7,7]
MASTER_colnames = ["stid", "code", "name", "type", "publisher_stid",
"publisher_code", "publisher_name", "writer"]
MASTER_coltypes = ["int", "str", "str", "sel", "int", "str", "str", "str"]
MASTER_choicecols = ["type"]
MASTER_choices = [[("Comedy", "Comedy"), ("Action", "Action"),
("Fiction", "Fiction"), ("Fantasy", "Fantasy"),
("Adventure", "Adventure")]]
MASTER_schfill = {"publisher_code":["ModalPublisher:code",
"publisher_stid:stid,publisher_name:name"]}
class SubForm(FlaskForm, CuneiSubForm):
stid = StringField("")
code = StringField("", validators=[InputRequired("Required!")])
name = StringField("", validators=[InputRequired("Required!")])
type = SelectField("")
publisher_stid = StringField("Publisher StID",
validators=[InputRequired(lazy_gettext("Required!"))],
render_kw={"readonly":""})
publisher_code = StringField("Publisher Code",
validators=[InputRequired(lazy_gettext("Required!"))])
publisher_name = StringField("Publisher Name", render_kw={"readonly":""})
writer = StringField("", validators=[InputRequired("Required!")])
def validate_writer(self, writer):
if len(writer.data) <= 1:
raise ValidationError(lazy_gettext("Very short"))
ฟังก์ชั่นการใช้งานและตัวแปร
- คำสั่งในการใช้งาน สามารถเข้าไปดูวิธีสร้างตารางแบบละเอียดได้ที่ CuneiTable#Define
'MASTER_colsizes': ความกว้างของช่องข้อมมูล(ทุกคอลัมน์ที่อยู่ในdatabaseต้องมีช่องข้อมูลเป็นของตัวเองทั้งหมด แต่สามารถใส่ค่าเป็น 0 ได้ หากไม่อยากให้ผู้ใช้เห็น)'MASTER_colnames': หัวคอลัมน์ที่ผู้ใช้เห็น(ไม่จำเป็นต้องตรงกับที่อยู่ในdatabase)'MASTER_coltypes': ชนิดของช่องข้อมูล'MASTER_choicecols': กำหนดช่องข้อมูลที่จะทำเป็นDrop Drown'MASTER_choices': ข้อมูลที่อยู่ใน Choices'MASTER_schfill': กำหนดช่องข้อมูลที่จะใช้ค้นหาโดยการขึ้นหน้าต่างใหม่'lazy_gettext': แปรช่องนี้ให้เป็นภาษาที่มนุยษ์เข้าใจ'class SubForm': เป็นเรื่องของ Form กรุณาไปดูที่ CuneiForm#Define
routes.py
html.py
มีส่วนประกอบที่สำคัญดังนี้
{% import "cunei_forms.html" as CuneiForms with context %}
{% import "cunei_tables.html" as CuneiTables with context %}
{% import "cunei_modals.html" as CuneiModals with context %}
{% extends "layout.html" %}
{% block content %}
{{ CuneiTables.general_table_block(table, block_height) }}
{{ CuneiTables.general_table_tail(table) }}
{% endblock content %}
{% block jvs %}
{% endblock jvs %}
'block content/endblock content': เปิด/ปิดส่วนที่แสดงเนื้อหา ในที่นี้เนื้อหาจะเป็นการแสดง Table- เป็นวิธีสร้าง Table แบบมารตราฐาน
{{ CuneiTables.general_table_block(table, block_height) }} {{ CuneiTables.general_table_tail(table) }}
'block jvs/endblock jvs': เปิด/ปิดส่วนที่การแสดงเนื้อหาjavascript
routes.py
วิธีตรง
@cunei_pos.route("/anime", methods=["GET", "POST"])
def anime():
table = Anime_Table(prefix="anime_table", perm_bit=4, editable=True,
populate_route=url_for("cunei_pos.test_table", fetch="yes"),
post_route=url_for("cunei_pos.test_table", is_submit="yes"))
publisher_tb = Publisher_Table(prefix="PublisherTable", searchable=True, editable=True,
populate_route=url_for("cunei_pos.publisher", fetch="yes"),
post_route=url_for("cunei_pos.publisher", is_submit="yes"),
in_modal="ModalPublisher", modal_head=lazy_gettext("Choose Publisher"),
perm_bit=4)
table.sch_tbs = [publisher_tb]
dbpath = path.join(session["company"]["datadir"], "table", "anime.db")
db = SqliteExtDatabase(dbpath, pragmas={"journal_mode":"wal"})
if request.args.get("fetch", False) == "yes":
if request.form.get("mode") == "qsch":
return jsonify({"data":['code':xxxxx, 'name':xxxx], "qs_status":"success"})
if request.form.get("mode") == "ncol":
return jsonify({"data":['code':xxxxx, 'name':xxxx]})
if request.form.get("mode") == "sel":
return jsonify({"data":['code':xxxxx, 'name':xxxx]})
if request.args.get("is_submit", False) == "yes":
dbpath = path.join(session["company"]["datadir"], "table", "publisher.db")
db = SqliteExtDatabase(dbpath, pragmas={"journal_mode":"wal"})
model_fields = Publisher._meta.fields.keys()
vals = {cn:getattr(table.form, cn).data for cn in table.cnames if cn in model_fields}
vals.pop("id", None)
isid = int(table.form.id.data)
isdel = table.form.is_del.data
with db.bind_ctx([Publisher]):
if isdel: // delete case
qry = Publisher.delete().where(Publisher.id==isid).execute()
return jsonify(dict(data="success", flash_messages=[], entry="deleted"))
elif isid == -1: // add case
new_entry = Publisher.create(**vals)
else: // edit case
try:
new_entry = Publisher.select().where(Publisher.id==isid)
except DoesNotExist:
new_entry = Publisher.create(**vals)
else:
for k,v in vals.items():
setattr(new_entry, k, v)
new_entry.save()
return_entry = [getattr(new_entry, cn, None) for cn in table.cnames]
return jsonify(dict(data="success", flash_messages=[], entry=return_entry))
else:
return jsonify(dict(err=table.form.errors, flash_messages=[]))
return render_template("cunei_pos/anime_table.html", table=table)
- ตัวแปร
'prefix': ชื่อที่ใช้ตั้งเป็นIDของTable (ต้องตั้งชื่อไฟล์HTMLเป็นชื่อนี้ด้วย)'perm_bit': สิทธิ์ในการเข้าถึงหน้านี้- - 4 สามารถ เพิ่ม/แก้ไข/ลบ
- - 3 สามารถ เพิ่ม/แก้ไข
- - 2 สามารถ เพิ่ม
- - 1 สามารถ ดูได้เพียงอย่างเดียว
'editable': สถานะปุ่มแก้ไขใน Table Anime จะทำการdimหรือไม่'populate_route': เปิดให้มีการrefetchหน้าใหม่- - argument ที่1: แสดงถึงหน้าที่ต้องการให้มีการrefetch
- - argument ที่2: เป็นคำสั่งยืนยันการrefetch
'post_route': เรียกใช้mode Submit บันทึกลงdatabase- - argument ที่1: แสดงถึงหน้าที่ต้องการให้มีการบันทึก
- - argument ที่2: เป็นคำสั่งยืนยันการบันทึก
'searchable': สถานะปุ่มค้นหาใน Table Publisher จะทำการdimหรือไม่'suppress_copy': สถานะปุ่มคัดลอกใน Table Publisher จะทำการdimหรือไม่'in_modal': Tableนี้ใช้โครงสร้าง database ตัวไหน'modal_head': ชื่อที่จะอยู่บนหัวpopup'request.args.get': ถ้าเป็นโหมด'fetch'(มีการเคลื่อนไหวของข้อมูล แต่ยังไม่ได้เข้าdata base)จะมีอยู่ 3 กรณี- 1. "qsch" กรณีนำเข้าข้อมูลจากการค้นหา (ครั้งแรกที่มีการเปิดpageขึ้นมา จะต้องเรียกข้อมูลทั้งหมดมาใส่ในtable)
- 2. "ncol" กรณีเพิ่มข้อมูลขึ้นบันทักใหม่
- 3. "sel" กรณีเลือกข้อมูล
'request.args.get':ถ้าเป็นโหมด'is_submit'(มีข้อมูลที่จะต้องเข้าdata base หรือมีกิจกรรมที่ต้องยุ่งกับdata base)สามารถเปิด data base แล้วดำเนินการได้เลย'table.form': เป็น Obj form ที่เก็บ field ทั้งหมดในหน้านี้ไว้ และใน แต่ละfield ก็จะเก็บ dataของตัวเองไว้'getattr(x,y).data': ดึงข้อมูลจาก Obj มาใช้'setattr': วิธีบันทึกของลงในObj'table.cnames': เก็บชื่อคอลัมน์ที่มีทั้งหมดในdatabase + submit(สถานะการบันทึก) + del_submit(เป็นปุ่มที่เมื่อกดมันจะไปบังคับติ๊กถูกที่is_delอีกที)และ is_del(สภานะการลบ)
- ขยายcode
- เก็บ Obj Table ที่จะแสดงบนหน้าจอ สามารถเพิ่ม Obj Table ได้มากกว่า 1 Table
publisher_tb = ...
- sectionทำงานอยู่ 2 แบบคือ
request.arge.get()
- - fetch : จะทำงานทุกครั้งที่มีการreload หน้า(ทุกการreloadจะมีการดึงข้อมูลใหม่ทุกครั้ง)
- - is_submit : จะทำงานก็ต่อเมื่อมีการบึนทึกของลงdatabase
- โหมดในการทำงานมีทั้งหมด 4 โหมด คือ
if request.form.get("mode")
- - sch : โหมดการค้นหาข้อมูล กรณีกดปุ่มค้นหา สามารถreturnได้ 2 แบบ
- -
returnjsonify({"data":['code':xxxxx, 'name':xxxx], "qs_status":"success"}) คือ returnผลลัพธ์ที่มีตัวเดียว - -
returnjsonify({"data":[['code':xxxxx, 'name':xxxx],...]}) คือ returnผลลัพธ์ที่มีหลายตัว
- -
- - ncol : โหมดใส่ค่าเริ่มต้นตามdatabaseนั้นๆ
- -
returnjsonify({"data":['code':xxxxx, 'name':xxxx]}) คือ returnผลลัพธ์ที่มี
- -
- - sel : โหมดการเลือกบรรทัด
- -
returnjsonify({"data":['code':xxxxx, 'name':xxxx]}) คือ returnผลลัพธ์ที่มี
- -
- - grab : โหมดการค้นหาข้อมูล กรณีที่co-database นำstidไปหาค่า
- -
returnjsonify({"data":['code':xxxxx, 'name':xxxx]}) คือ returnผลลัพธ์ที่มี
- -
- - sch : โหมดการค้นหาข้อมูล กรณีกดปุ่มค้นหา สามารถreturnได้ 2 แบบ
วิธีลัด
@cunei_pos.route("/anime", methods=["GET", "POST"])
def anime():
table = Anime_Table(prefix="anime_table", suppress_copy=False, editable=True)
table.sch_tbs = [Publisher_Table(prefix="PublisherTable", perm_bit=4, editable=True,
populate_route=url_for("cunei_pos.publisher", fetch="yes"),
post_route=url_for("cunei_pos.publisher", is_submit="yes"),
in_modal="ModalPublisher", modal_head=lazy_gettext("Choose Publisher"))]
dbtype, tbname = "table", "anime"
model, module = Anime, "cunei_pos"
new_default = get_default_anime
fetch_all = anime_grab_all
return single_tb_page(dbtype, tbname, model, module, table, new_default, fetch_all, request, forced_perm=4, in_modal="ModalPublisher", modal_head=lazy_gettext("Choose Publisher"))]
วิธีนี้จะช่วยทำให้โค้ดดูสั้นขึ้นและอ่านง่ายขึ้น โดยกำหนดแค่ค่าเริ่มต้น และรวบรวมตัวแปรให้ไปsetไว้ในที่เดียวกัน (ขั้นตอนในการดำเนินการเหมือนกัน เพียงแค่ย่อมันให้สั้นลงเท่านั้น)
get_default_anime
ฟังก์ชั่นนี้ จะทำหน้าที่จัดการค่าเริ่มต้นต่างๆที่ระบบควรมี
anime_grab_all
ฟังก์ชั่นนี้ จะทำหน้าที่ดึงข้อมูลมีระบบควรจะรู้ออกมา
new_default = get_default_anime
เป็นfunction Obj ที่จะคืนผล default กลับมา โดยปกติแล้วจะเขียนfunctionนี้ในไฟล์ functions.py
def get_default_anime(): return dict(stid=get_next_stid("Anime", Anime), id=-1)
fetch_all = anime_grab_all
เป็นfunction Obj ที่จะคืนผลการselect โดยปกติแล้วจะเขียนฟังชั่นก์นี้ในไฟล์ functions.py
def anime_grab_all(qsch_spec={}): tb_db = get_db("table", "anime", auto_create=[Anime]) crits = get_where(Anime, qsch_spec) with tb_db.bind_ctx([Anime]): if len(crits) == 0: return Anime.select().order_by(fn.LOWER(Anime.code)) else: return Anime.select().order_by(fn.LOWER(Anime.code)).where(*crits)
สร้าง Form - non Database
เป็นหน้าที่แสดง Form อย่างเดียว โดยไม่มีการบันทึกของลงdatabase
<image2>
- สิ่งของที่จำเป็นในการสร้างมี
- สร้างดาต้าเบส: จะถูกเขียนอยู่ใน model.py
- สร้างองค์ประกอบตัวฟอร์ม(ฟอร์มต้องมีรูปร่างหน้าตาอย่างไร ต้องโชว์คอลัมน์อะไรบ้าง): จะถูกเขียนอยู่ใน form.py
- สร้างหน้าHTML: จะถูกเขียนอยู่ใน html.py
- สร้างท่องที่เชื่อมระหว่าง UX-UI เราจะเรียกท่อนนี้ว่า route จะถูกเขียนอยู่ใน routes.py
- ถ้าFormมีการคิดวิเคราะห์ที่ลึกมากขึ้น ไม่สามารถเขียนใน routes.py ได้ ให้ไปเขียนไว้ใน functions.py แทน
โดยไฟล์ที่จำเป็นมีดังนี้
model.py
model.py จะเป็นตัวกำหนดโครงสร้างดาต้าเบส โดยชื่อคาสจะเอาไปตั้งเป็นชื่อตารางดาต้าเบส และตัวแปรแต่ละตัวเปรียบเสมือนfield ที่จะต้องกำหนดชนิดตัวแปรเข้าไป (สามารถไปดูวิธีการสร้างModelแบบละเอียดได้ที่cunei#...)
class Foreigner(CuneiModel):
id = AutoIncrementField()
passport = CharField()
first_name = CharField(default="")
middle_name = CharField(default="", null=True)
last_name = CharField(default="", null=True)
birthday = CharField()
saving = FloatField()
gender = CharField()
status = CharField()
spouse_first_name = CharField(default="", null=True)
spouse_middle_name = CharField(default="", null=True)
spouse_last_name = CharField(default="", null=True)
address = TextField(default="", null=True)
pict = CharField(null=True)
class Meta:
database = SqliteExtDatabase(None)
ฟังก์ชั่นการใช้งานและชนิดช้อมูล
- คำสั่งในการใช้งาน
'AutoIncrementField()': กำหนดให้มีรันตัวเลขอัตโนมัติ'null': กำหนดให้databaseสามารถเว้นว่างได้'SqliteExtDatabase': เปิดdatabase
- กำหนดชนิดของข้อมูล
'IntegerField()': ตัวแปรชนิดตัวเลขที่เป็นจำนวนเต็ม'FloatField()': ตัวแปรชนิดตัวเลขที่จุดทศนิยม'CharField()': ตัวแปรชนิดที่เป็นตัวอักษร'TextField()': ตัวแปรชนิดที่เป็นตัวอักษรและช่องข้อความขยายได้'DateField()': ?
form.py
class Foreigner_Form(FlaskForm, CuneiForm):
id = StringField("ID")
passport = StringField(lazy_gettext("Passport"),
validators=[Length(8,8,lazy_gettext("Wrong passport number"))])
first_name = StringField(lazy_gettext("First Name"),
validators=[InputRequired(lazy_gettext("Who the fack are you"))])
middle_name = StringField(lazy_gettext("Middle Name"))
last_name = StringField(lazy_gettext("Last Name"))
birthday = StringField(lazy_gettext("Birthday"),
validators=[InputRequired(lazy_gettext("Birthday la?"))])
saving = StringField(lazy_gettext("Saving"))
gender = RadioField(lazy_gettext("Gender"),
choices=[("male", lazy_gettext("Male")),
("female", lazy_gettext("Female")),
("other", lazy_gettext("Other"))],
default="male")
status = RadioField(lazy_gettext("status"),
choices=[("single", lazy_gettext("Single")),
("widow", lazy_gettext("Widowed")),
("married", lazy_gettext("Married"))],
default="single")
spouse_first_name = StringField(lazy_gettext("Spouse first name"))
spouse_middle_name = StringField(lazy_gettext("Spouse middle name"))
spouse_last_name = StringField(lazy_gettext("Spouse last name"))
address = TextAreaField(lazy_gettext("Address"))
pict = FileField(lazy_gettext("Photo"))
pict_isdel = BoolField("Pict: isdel", render_kw={"readonly":""})
submit = SubmitField("Submit")
is_del = BoolField("is_del")
del_submit = SubmitField("Delete")
MASTER_sptypes = [("id", "int"), ("birthday", "date"), ("saving", "amt"),
("pict", ["img", "table", "foreigner"])]
def validate_spouse_first_name(self, spouse_first_name):
if self.status.data == "married" and spouse_first_name.data == "":
raise ValidationError(lazy_gettext("What is your spouse's name"))
def validate_saving(self, saving):
try:
test_money = float(saving.data)
except (TypeError, ValueError):
test_money = 0.0
if test_money < 80000.0:
raise ValidationError(lazy_gettext("Eww! Peasant!"))
def validate_birthday(self, birthday):
bth = date_from_dbstr(birthday.data)
if bth > datetime.now():
raise ValidationError(lazy_gettext("You are embryo"))
if datetime.now().year - bth.year >150:
raise ValidationError(lazy_gettext("You're really old"))
สร้าง filedที่ ต้องการใช้ในหน้า Form นี้ขึ้นมา พร้อมทั้งกำหนดชนิดของตัวแปร
ฟังก์ชั่นการใช้งานและชนิดช้อมูล
- คำสั่งในการใช้งาน
'lazy_gettext': แปรช่องนี้ให้เป็นภาษาที่มนุยษ์เข้าใจ'validators': สร้างเงื่อนไขให้กับช่องเช่น วันที่ที่กรอกต้อง ไม่น้อยกว่าหรือมากกว่าเท่านี้นะ'InputRequired': แจ้งErrorหากไม่ผ่านเงือนไขที่ให้
- ตัวแปรและชนิดข้อมูล
'StringField()': ตัวแปรชนิดที่เป็นตัวอักษร'TextField()': ตัวแปรชนิดที่เป็นตัวอักษรและช่องข้อความขยายได้'IntegerField()': ตัวแปรชนิดตัวเลข'FloatField()': ตัวแปรชนิดตัวเลขที่จุดทศนิยม'BoolField()': ตัวแปรชนิดTrue/False'RadioField()': ตัวแปรชนิดติ๊กถูก'choices': ตัวเลือกของ RadioField'default': ค่าเริ่มต้นของ RadioField
'FileField': ตัวแปรชนิดไฟล์'SubmitField': ตัวแปรสร้างปุ่มของ Form นั้นๆ
- ขยาย Code
- เรื่องการใส่รูปกรุณาไปดูรายละเอียดได้ที่ CuneiForm#File_&_Image_Fields
pict = FileField(lazy_gettext("Photo")) pict_isdel = BoolField("Pict: isdel", render_kw={"readonly":""})
- เป็นการสร้างปุ่ม Submit และ Delete ส่วน is_del เป็น check box ที่ไม่ได้เปิดให้เห็น ทุกครั้งที่กดปุ่ม Delete check box จะเปลี่ยนเป็น true โดยทันที ระบบข้างในจะทำงานกับ check boxตัวนี้แทนปุ่ม Delete เพราะว่าปุ่ม Submit และปุ่ม Delete ไม่สามารถใช้งานภายในFormเดียวกันได้ จึงต้องมีcheck boxมารับค่าแทน
submit = SubmitField("Submit") del_submit = SubmitField("Delete") is_del = BoolField("is_del")
- แปลงชนิดตัวแปรก่อนเอาเข้า database, จาก field ที่โชว์อยู่บนหน้าจอ field พวกนี้จะเป็นตัวแปรชนิด String ทั้งหมด เพราะอย่างนั้นเวลาจะบันทึกเข้า database จึงต้องแปลงชนิดตัวแปรสะก่อน
MASTER_sptypes = [("id", "int"), ("birthday", "date"), ("saving", "amt"), ("pict", ["img", "table", "foreigner"])]
- ชนิดตัวแปรที่เป็นตัวเลข
- int: เลขจำนวนเต็ม
- amt: ตัวเลขระบุที่เกี่ยวกับยอดเงิน
- acc: ตัวเลขที่ไว้ลงบัญชี
- pct: จำนวนที่เป็นพวกร้อนละ/เปอร์เซ็น
- qty: ตัวเลขที่เกี่ยวกับจำนวน/หน่วย
- สามารถไปอ่านเพิ่มเติมได้ที่ CuneiForm#Available_Types
- ชนิดตัวแปรที่เป็นตัวเลข
- เป็น function สร้างเงื่อนไขให้กับ field เช่น วันที่ที่กรอกต้องไม่น้อยกว่าหรือมากกว่าเท่านี้นะ โดยวิธีการตั้งชื่อให้ใส่ validate ไว้ข้างหน้าแล้วตามชื่อ _ชื่อ field ที่ต้องการสร้างเงื่อนไขให้ แล้วส่ง argument selfและชื่อfieldมาด้วย
def validate_spouse_first_name(...) def validate_saving(...) def validate_birthday(...)
html.py
{% import "cunei_forms.html" as CuneiForms %}
{% import "cunei_tables.html" as CuneiTables with context %}
{% import "cunei_modals.html" as CuneiModals with context %}
{% import "cunei_scripts.html" as CuneiScripts %}
{% extends "greeter_slim.html" %}
{% block content_wgreeter %}
<div class="col-sm-11">
<div class="card border-dark mb-3" style="max-width:100%;">
<div class="card-body py-0">
{{ CuneiForms.general_form_head(form, is_master=true) }}
<div class="row align-items-start pt-3">
<div class="col-lg-2">
<div class="row align-items-start">
<div class="col"><img id="{{ form.pict.id }}_imgbox"
style="width:100%;"></div>
</div>
<div class="row align-items-start">
{{ CuneiForms.render_check(form, form.pict_isdel, hidden=true) }}
<div class="col">{{ CuneiForms.render_upload(form, form.pict) }}</div>
</div>
</div>
<div class="col-lg">
<div class="row align-items-start">
{{ CuneiForms.render_blank(form, form.id, hidden=false) }}
<div class="col">{{ CuneiForms.render_blank(form,
form.passport) }}</div>
</div>
<div class="row align-items-start">
<div class="col">{{ CuneiForms.render_blank(form,
form.first_name) }}</div>
<div class="col">{{ CuneiForms.render_blank(form,
form.middle_name) }}</div>
<div class="col">{{ CuneiForms.render_blank(form,
form.last_name) }}</div>
</div>
<div class="row align-items-start">
<div class="col-4">{{ CuneiForms.render_blank(form,
form.birthday) }}</div>
<div class="col">{{ CuneiForms.render_blank(form,
form.saving) }}</div>
</div>
<div class="row align-items-start">
<div class="col-5">{{ CuneiForms.render_radio(form, form.gender,
col=3, force_slim=true, label_width="105px") }}</div>
</div>
<div class="row align-items-start">
<div class="col-5">{{ CuneiForms.render_radio(form,
form.status, col=3, force_slim=true) }}</div>
</div>
<div class="row align-items-start">
<div class="col">{{ CuneiForms.render_blank(form,
form.spouse_first_name) }}</div>
<div class="col">{{ CuneiForms.render_blank(form,
form.spouse_middle_name) }}</div>
<div class="col">{{ CuneiForms.render_blank(form,
form.spouse_last_name) }}</div>
</div>
<div class="row align-items-start">
<div class="col">{{ CuneiForms.render_blank(form,
form.address) }}</div>
</div>
<div class="row align-items-start">
<div class="col-0 text-right">{{ CuneiForms.render_check
(form, form.is_del, hidden=true) }}</div>
<div class="col-0 text-right">{{ CuneiForms.render_submit
(form, form.del_submit, class="danger") }}</div>
<div class="col-0 text-right">{{ CuneiForms.render_submit
(form, form.submit) }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{ CuneiForms.general_form_tail(form, true, pack_del=true) }}
{% endblock content_wgreeter %}
{% block greeter_jvs %}
{% endblock greeter_jvs %}
- Tag ต่างๆที่ควรรู้
- - general_form_head / general_form_tail : Tagเปิด/ปิดส่วนของform ช่องข้อมูลทั้งหมดที่แสดงจะต้องอยู่ระหว่าง 2 บรรทัดนี้
- - <img id="form.pict.id"> : หากข้อมูลที่ใส่เป็นข้อมูลประเภทรูปภาพ ให้ใช้Tagนี้
- - render_check : เป็น Tag ที่สร้างcheck box
- - render_upload : เป็น Tag ที่สร้างแถบอัพโหลดไฟล์
- - render_blank : เป็น Tag ที่สร้างช่อง label และหัวของlabel
- - render_radio : เป็น Tag ที่สร้าง Text redio
- - render_submit : เป็น Tag ที่สร้างปุ่มsubmit ทุกครั้งที่สร้างฟอร์มจำเป็นต้องมี
- - block greeter_jvs / endblock greeter_jvs : เป็น Tag ที่ไว้สำหรับเขียน javascript
routes.py
@cunei_pos.route("/foreigner", methods=["GET", "POST"])
def foreigner():
form = Foreigner_Form.init_form(prefix="foreigner_form",
populate_route=url_for("cunei_pos.foreigner_form", fetch="yes"),
populate_id=["passport"],
post_route=url_for("cunei_pos.foreigner_form", issubmit="yes"))
if request.args.get("issubmit", False) == "yes":
msg = ""
flashers = []
if form.validate_on_submit():
gender = "Woman" if new_entry.gender == "female" else "Man"
msg = "welcome to Thailand"+ new_entry.first_name + gender
sh = [msg, "success"]
data = "success"
else:
msg = "Go to jail"+ new_entry.first_name + gender
sh = [msg, "danger"]
data = "failure"
flashers.append(sh)
return jsonify(dict(data=data,err=form.errors,flash_messages=flashers))
form.id.data = 1
return render_template("cunei_pos/foreigner.html, form=form")
ตัวแปร
.......
เมื่อถึงตรงนี้ Form ที่คุณทำจะสามารถ
- - กรอกข้อมูลเข้าได้
- - จับ Error ได้
แต่ไม่สามารถ
- - บันทึกข้อมูลเข้า database ได้ เพราะไม่ได้สร้าง databaseไว้
- - เรียกดูข้อมูล