اذهب إلى المحتوى

Mohamed Abu Abdo

الأعضاء
  • المساهمات

    31
  • تاريخ الانضمام

  • تاريخ آخر زيارة

أجوبة بواسطة Mohamed Abu Abdo

  1. من الممكن جعل المتغير المذكور عام Global ولكنه مع الوصول المتزامن للمستخدمين لن تتمكن من مراقبة أو إستخدام متغيراتك بشكل جيد,أو بوصف أدق عندما يقوم عدة مستخدمين في نفس اللحظة بتسجيل الدخول سيتم طباعة القيم في نفس اللحظة ,وإذا كنت تريد إستخدام هذا الرقم كتعريف لجلسة المستخدم فلن تتمكن من معرفة الرقم الخاص بكل مستخدم.

    لذلك فكر في أساليب أخرى علي سبيل المثال إذا كانت الدالتين يتجاوبان مع نفس صفحة html فيمكنك إرسال القيمة من الدالة الأولى إلى صفحة html حيث يتم الاحتفاظ بالمتغير هناك ضمن حقل نص أو text box, ثم تقوم الدالة الثانية بالحصول عليه عندما تستلم طلب Post عبر 

    random_number = request.form['random_number']

    إليك المثال الكامل:

    login.py

    <html>
       <body>      
         <form action = "/login" method = "post">
            <p>Enter Name0:</p>
            <p><input type = "text" name = "nm" /></p>
            <input type="text" style="display:none;" name="random_number" value="{{random_number}}">
            <p><input type = "submit" value = "submit" /></p>
          </form>      
       </body>
    </html>

    لاحظ هنا قمنا بإخفاء مربع النص Text box

    style="display:none;"

    وهذا إذا كنا لا نحتاج لعرضه على المستخدمين,

    main.py

    from flask import Flask, render_template, request
    import random
    
    app = Flask(__name__, template_folder='template')
    
    
    @app.route('/login', methods=['GET']) #وهي التي تعمل عند فتح الصفحةGETالدالتين يحملان نفس المسار بالفعل ولكن انتبه إحداهما 
    def a():
        random_number = random.random()
        return render_template("login.html", random_number=random_number)#إرسال المتغير أو بالأحرى قيمته
    
    
    @app.route('/login', methods=['POST'])#كتسجيل الدخول مثلا POSTوالثانية عند إرسال فورم عبر 
    def b():
        random_number = request.form['random_number'] #إستلام قيمة المتغير المنشود
        print("Random Number:{}".format(random_number))
        return render_template('home.html')
    
    
    
    if __name__ == "__main__":
        app.run(debug=True)

    من المؤكد بأن هناك المزيد والمزيد من الطرق, وأذكر منهم أيضا شئ مما أشرت إليه في نهاية سؤالك وهو الاستفادة من الجلسات وcookie فالكوكيز هي ببساطة تشبه وحدة تخزين مرتبطة بموقعك داخل متصفح كل مستخدم, حيث يمكننا الإحتفاظ بالمتغيرات أو token أو غير ذلك بداخلها, بغاية إفادة المستخدمين وجعل تجربتهم لموقعك أفضل وبالطبع هى ليست مناسبة لكل شئ, ولكن لكل مقام مقال, وعادة ما تكون مضطراً لإختيار الأداة التى تناسب مشروعك أو التحدى الذي يواجهك.

     

    • أعجبني 1
  2. الصور تستهلك الكثير من الذاكرة ,وبوضعها داخل قاعدة البيانات سيتسبب هذا بمرور الوقت في ضرورة تكبير الحيز المتاح لقاعدة البيانات هذا أولاً,

    ثانياً سيتسبب هذا في تأخر طلبات الإستعلام من وإلي قاعدة البيانات,

    ثالثا كبر حجم الاستعلام نفسه فعلي سبيل المثال إذا كان المستخدم سيطلب عرض Slides يحتوي علي عدة صور وليكن أربعة صور كل صورة منهم حجمها يفوق 500 كيلوبايت ,مما يعني 2 ميغابايت حجم الصور الإجمالي أو أكثر , عوضاً عن باقي البيانات , في هذا الوضع تخيل أن سرعة الانترنت لدي مستخدمين هذا الموقع أقل من 100 كيلوبايت في الثانية الواحدة أو لنفرض 100, وهذا يعني أن زمن تنفيذ واستلام هذا الطلب يترواح بين 15 إلى 25 ثانية ,في حين أنك قد تكون مضطراً إلى ضبط إعدادات موقعك والسيرفر الذي يديره علي زمن انتظار أو timeOut أقل من هذا الحيز بكثير , مما عني في نهاية الأمر فشل الطلب وظهور رسالة Error time out للمستخدمين الذين يمتلكون سرعة اتصال بالانترنت أقل من 500 كيلوبايت , لا أخفي عليك القول أنني في هذه اللحظة لا يمكنني الدخول إلى موقعك هذا لأنني أمتلك سرعة اتصال أقل من جيدة.

    غير ذلك كبر حجم الطلبات المرسلة يستغرق الكثير من الموارد المخصصة لموقعك او تطبيقك,

    البديل لتلك الطريقة هو حفظ اسماء الصور فقط في قاعدة البيانات , بينما يتم حفظ الصور في مكان أخر أو ربما سيرفر أخر تماما وهذا ما يقوم به العديد من المواقع ,فمثلا يمكنك إنشاء موقعك علي أي استضافة ثم حفظ الصور او الملفات علي اسضافة أخري أقل تكلفة وأفضل ك Amazon S3 ,

    وهنا حاول تخيل وضع المستخدم الذي يحاول تحميل موقع به العديد من الصور بالإضافة إلى Slides الموجود في رأس الصفحة الرئيسية, فمع حفظ جميع الصور في قاعدة البيانات مضغوطة بbase64 سيصبح من الصعب الوصول لموقعك.

    ولكن في حال وجود الصور بعيداً عن قاعدة البيانات سيكون تحميل كل صورة فقط عندما يحاول المستخدم عرضها , كأن يتحرك داخل Slides ,أو يحاول عرض اجزاء من الصفحة تحوي صوراً إلى أخره.

    ولكن نعود إلى نقطة مهمة ذكرتها في سؤالك"وهي إن كنا نقوم بتحويلها إلى base64 قبل الإرسال!"

    فأولاً من الأفضل تشفير الصور إلى base64 داخل frontend أو حتي عدم تشفيرها فذلك ليس ضرورياً, وربما في حالات معينة مثل محاولة اخفاء الخادم الذي يتم الاحتفاظ بالصور بداخله,

    حتي مع هذا الإجراء فأنت تقلل الاعتماد علي قاعدة البيانات في أمور بعينها , ولكن يظل هناك جانب مرهق واقع علي الخادم أو علي الجزء المعني بتحويل الصور وإرسالها للمستخدم.

    وإن لم يكن الجانب الأمني في اعتبارك فأنصح بإرسال url او لينك الصور بدلا من تحويلها, وبالتأكيد هناك الكثير من الأسباب التقنية التي لم أتطرق إليها.

    بالتوفيق

     

     

    Screenshot_440.png

    • أعجبني 1
  3. لا تحتاج إلي كتابة المسار داخل render_template فقط يمكنك تغيير المسار الرئيسي من خلال template_folder ,

    تابع التالي


     

    #main.py
    from flask import (Flask, render_template)
    
    app = Flask(__name__, template_folder='../frontend/template')
    
    
    @app.route('/notifications')
    def notifications():
        return render_template("view_notifications.html")
    
      
      
    #إذا كان لديك مسارات فرعية فيمكنك إستخدامها بالشكل التالي
    
    @app.route('/page')
    def pages():
        return render_template("post/page.html")
    
      

    وإذا كنت تشك في مسارك يمكنك كتابة المسار كاملاً بالشكل التالي

    app = Flask(__name__, template_folder=r'C:\Users\mohamed\project\frontend\template')

    لاتنسي حرف r مهم إذا كنت تستخدم نظام التشغيل ويندوز ومساره الكامل والذي يبدأ ب C:\Users أو D:\x وما شابه

    وإلا سيواجهك خطأ كهذا

        app = Flask(__name__, template_folder='C:\Users\mohamed\project\frontend\template')
                                             ^
    SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape

    موفق

    • أعجبني 1
  4. يمكن الحفظ بأكثر من طريقة مثلا من form.py او view.py أو models.py 

    فيما يلي طريقتين للحفظ من خلال models.py

    def edit_path(instance, filename):
        """من الممكن ألا يعمل 
           username
           ويكون البديل هو
           user"""
     
        users_media = 'users' #يرجي العلم بأن جميع المستخدمين سيتم إنشائهم بداخل هذا المسار الفرعي ويمكن استبداله 
        return f'{users_media}/{instance.username}/{filename}'
    
    
    class Post(models.Model):
        title = models.CharField(max_length=200)
        user = models.ForeignKey(User)
        thumbnail = models.FileField(upload_to=edit_path) #يتم استبدال مسار الحفظ بالدالة أعلاه

    سيتم إنشاء مجلد أو مسار لكل مستخدم بداخل users وكل هذا بالطبع داخل المسار MEDIA_ROOT المعلن عنه في settings.py .

    2:

    هو استخدام دالة save الموجودة في models ,تابع التالي

    from django.conf import settings
    from django.core.files.base import File
    import os
    
    
    class Post(models.Model):
        title = models.CharField(max_length=200)
        user = models.ForeignKey(User)
        thumbnail = models.FileField(upload_to='users/')
        def save(self, *args, **kwargs):#يتم تنفيذ هذه الدالة مباشرة عند إجراء حفظ بيانات جديدة لهذا الجدول
            if self.user:
               name = self.user.username #الحصول على أسم المستخدم
               #إنشاء مسار جديد لهذا المستخدم في مسار الوسائط
               new_directory = os.path.join(settings.MEDIA_ROOT, 'users', name) 
               if not os.path.exists(new_directory):
                  os.makedirs(new_directory)
               #قراءة الملف السابق 
               with open(thumbnail.path) as f:
                    #ثم حفظه في المسار الجديد وتسجيل التغيير في الحقل المنشود
                    self.thumbnail.save(os.path.join(new_directory,thumbnail.filename), File(f))
            return super(Post, self).save(*args, **kwargs)
          

    تعتبر الطريقة الأولي هي الأفضل ولكن الطريقة الثانية قد تكون مفيدة وأكثر مرونة في حالات عدة من بينها إستخدام إجراءات أكثر داخل الجدول Post من تغيير قيمة حقول أخري في نفس اللحظة, إلي أخره.

    موفقين

    • أعجبني 1
  5. تطبيقات Django تكون موجودة في عدة أماكن تحتاج منك إلي حذفها يدوياً, مثلا urls.py الرئيسي وINSTALLED_APPS وغيرها من الأماكن, 

    بالنسبة لقاعدة البيانات قد يختلف إزالة التطبيقات بين إصدارات Django الأحدث والأقدم فمثلا في الإصدارات الأقدم من 1.7 يمكننا استخدام 

    python manage.py sqlclear  myapp
    python manage.py migrate

    وبالنسبة للإصدارات الأحدث يكفي إزالة التطبيق من INSTALLED_APPS وتنفيذ

    python manage.py migrate

    -------------

    هناك طرق أخرى مثلا يمكن حذف الجداول المتعلقة بهذا التطبيق قبل إزالة التطبيق نفسه

    python manage.py makemigrations -n drop_all_tables myapp

    ثم إزالته بأمان من INSTALLED_APPS ,والمجلد الخاص به.

    ولكن يجب الانتباه جيداً فربما تكون تلك الجداول مستخدمه في تطبيقات أخري ,أو قد تكون هناك وظائف من التطبيق الحالي مستخدمة في باقي التطبيقات.

    • أعجبني 1
  6. لا يمكن الجزم بأمور تتعلق بالمستقبل فلا أحد يدري ما يخبأه غداً من تقنيات أو مفاجآت, ولكن Front-End برأيي مايزال موجود ومهم وربما الطلب كثيف حول العالم ولكن المسالة تتعلق بكثرة عدد مطوري الواجهة الأمامية Front-End , السبب الأخر والذي يجعل Front-End مهم وموجود هو التكامل بين أطر الويب و خدمات الويب web services أو API ,دعنا نعيد النظر قليلا في مهام مطور الواجهة الأمامية ونقسم عمله بشكل أعمق قليلاً,

    فمثلا التصميم هو جزء بسيط من العمل الموكل إلى مطور الFront-End ومن الممكن مؤخراً أن يتم إنجاز التصميم شخص أخر لا علاقة له بFrontEnd, نظراً لأنه مسؤول عن إرسال و إستلام الطلبات من وإلى الخادم أو api ,وربطه مع واجهة الموقع مثلا وعناصره المختلفة ,بالنظر إلى جزء هين من عمله وما يطلب منه يتضح أنه ليس من السهل الاستغناء عن كل شئ ,فمثلا تتنافس ريكآت وفيو react ,vue.js وغيرهم علي مكانة مميزة بين أطر الويب ,ولكن كلاهما يعتمد علي js وما يقدمانه هو مجرد تيسيير لعمل Front-End,

    إذا حدث واختفي أحد أطر الويب المهمة فمن المؤكد أن هناك بديل أفضل وأسهل منه قد ظهر ,وبالطبع يجب تعلم التنقل إلى الأدوات الأفضل و الأنسب لك والأنسب للعمل الذي تقوم به في كل زمان ومكان.

    وأيضا مسألة أخرى مهمة وهي عدد أطر الويب الموجودة حالياً والتنافس بينهم,

    وفي النهاية أعتقد بأن المجالات كافة تتغير بسرعة كبيرة للغاية ومن المؤكد أن مطور الواجهة الأمامية المتعارف عليه أصبح يمتلك وظائف ومهام مختلفة عن ذي قبل.

    ويجب علينا مواكبتها.

    بالتوفيق علي أي حال.

  7. يبدو الكود مكتمل بنسبة جيدة ولكن تحتاج إلى استيراد مكتبات أخرى في head أعني تحديداً jquery

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

    ولكن أيضا لماذا تريد ان يتم إعادة توجيهك إلى صفحة أخري هنا؟

    return render_template('start_page.html')

    لدي اقتراح آخر وهو استخدام صفحة واحدة فقط وإذا كان المستخدم يبحث فسيتم الرد ب json يحوي بداخله list المقترحات ,ويتم عرضها في جدول بسيط قابل للتحديث عند محاولة الكتابة في كل مرة وليس من خلال زر او Button,

    إليكم المثال التالي:ونبدأ ب main.py :

    #main.py
    from flask import (Flask, render_template, request, jsonify)
    from bs4 import BeautifulSoup
    import requests
    
    
    app = Flask(__name__, template_folder='template')
    
    @app.route('/search', methods=['GET', 'POST']) #إستلام الطلبات من جيت وبوست
    def suggest_data():
        if request.method == 'GET' and request.args.get('jsdata'): #إذا كان هناك داتا في الطلب نفذ ما بداخل الشرط
           query = request.args.get('jsdata')
           if query == None:
               suggestions_list = ['',]
           else:
               suggestions_list = []
               
               r = requests.get('http://suggestqueries.google.com/complete/search?output=toolbar&hl=ru&q={}&gl=in'.format(query), 'lxml')
               soup = BeautifulSoup(r.content)
               suggestions = soup.find_all('suggestion')
               
               for suggestion in suggestions:
                   suggestions_list.append(suggestion.attrs['data'])
           return jsonify(data=suggestions_list) #إعادة الداتا كجسون وليس صفحة ويب آخري
        else:
           #إن لم يتحقق الشرط سيتم إعادة صفحة ويب وهي صفحة البحث
           return render_template('start_page.html')
    
    if __name__ == "__main__":
        app.run(debug = True)

    في كل الأحوال عند الدخول إلي الصفحة من خلال المتصفح سوف يرسلنا إلى render_template('start_page.html') والسبب هو عدم وجود jsdata إلا في حال تم الارسال من ajax.

    نأتي إلى صفحة start_page.html

    <!DOCTYPE HTML>
    <html>
    <head>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <!-->مهم إضافته</-->
      <title>Google Suggest Tool Flask</title>
    </head>
    <body>
       <h1>Search: Google Suggest Tool Flask</h1>
      
       <form action=""> <!--> من المهم إضاقة فورم لمستطيل البحث</-->
           <input id="search_input" type="text" name="typeing" placeholder="Country">
       </form>
    
       <h1>Result</h1>
       <table id="myTable"></table> <!-->إنشاء جدول لعرض البيانات أو المقترحات</-->
       
       <script>
            $("#search_input").keyup(function(){
              var some_var = $(this).val(); 
                 $.ajax({
                        url: "/search", //رابط دالة البحث
                        type: "get",
                        data:{jsdata: some_var},
                        success: function(response) {
                           var res = response["data"]; //سحب المقترحات من الرد القادم من دالة البحث
                           var table = document.getElementById("myTable"); / تهيئةالجدول لعرض الداتا
                           //قبل العرض نقوم بحذف  محتوي الجدول من عملية البحث السابقة
                           while(table.rows.length > 0) {
                             table.deleteRow(0);
                           }
                           //لووب علي المقترحات 
                           for (var a=0; a < res.length; a++) {
                               //يتم إنشاء صف داخل الجدول لكل مقترح
                               table.innerHTML += "<tr>"+"<td>"+a+"</td>"+"<td>"+res[a]+"</td>"+"</tr>";
                           }
                        }
                        
                  });
            });
       </script>
        
    </body>
    </html>

    تابع النتائج التالية:

    Screenshot_425.thumb.png.235e43fef792e507cbd6a6ecec80c73b.pngScreenshot_426.thumb.png.23c3f49416c8ec4c9c088be1e5149678.pngScreenshot_427.thumb.png.22dc870bd93762ef39859bd38ac0a3ad.png

    كما ترى لا نحتاج إلى صفحة جديدة أو إعادة تحميل الصفحة الحالية لعرض البيانات القادمة.

    يمكنك التفكير في المزيد كعرض المقترحات ضمن text box وقائمة منسدلة تسهل عليك البحث ,انظر المثال التالي

    Screenshot_428.png.e9494299bc2a97efd77316f7f611d516.png

    ستحتاج إلى مراجعة المصدر التالى "text box autocomplete" لمعرفة كيف يتم إنجاز بعض المهام من خلال css وقليل من js.

    موفقين.

    • أعجبني 1
  8. أجل يمكنك إضافة صفحات لعناوين url المجهولة والغير متاحة في مشروعك ,أو أي خطأ.

    تابع التالي

    أولا في ملف settings.py يجب إلغاء وضع التصحيح أو التطوير {Debugging}, من خلال تمرير False إلى DEBUG بدلا من True

    DEBUG = False
    
    ALLOWED_HOSTS = ['*']
    
    # Application definition

    ثانيا يجب إنشاء ملفين html للأخطاء 404 و 500 في مجلد templates سنفترض أن أسمائهما كما يلى:

    404.html
    500.html

    لك كامل الحرية في محتوى تلك الصفحات ولكن إليك مثال بسيط لصفحة 404:

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>CodePen - 404 Concept Page @property</title>
    </head>
    <body>
    
    <h1>404</h1>
    <div class="info">
      <h1>Error: {{status}} </h1>
      <h2>We can't find that page</h2>
      <a href="/app" target="_blank">Go To Home</a> <!-->اذهب إلى الصفحة الرئيسية </-->
    </div>
    
    </body>
    </html>

    ثالثا إضافة دالتين إلي ملف views.py 

    from django.shortcuts import render
    
    # Create your views here.
    
    
    def index(request):
        """دالة الصفحة الرئيسية """
        return render(request, 'index.html')
    
      
    def error_404(request, exception):
        """دالة يتم تنفيذها في حال وجود خطأ404"""
        return render(request,'404.html', status=404)
    
    def error_500(request, *args, **argv):
        """نفس الدالة السابقة ولكن يتم تنفيذها في حال وجود خطأ يشير إلى الكود500"""
        return render(request, '500.html', status=500) #تأكد من كتابة اسم الملف أو القالب بشكل صحيح 

    أخيرا ستقوم بإستدعاء هاتين الدالتين بداخل ملف urls.py الخاص بمشروعك ,أو تطبيقك ولكنه في كل الأحوال سيعمل.

    اقتباس

    تذكير: يوجد ملف urls.py في المشروع الرئيسي بجوار ملفات settings.py و wsgi.py وهناك ملف urls.py أخر أو أكثر يتم إنشائه عادة في التطبيقات الفرعية أو ال Apps الموجودة داخل Django.

    يمكنك اضافة الجزء التالي إلى واحد من تلك الملفات وإن كنت أرجح كتابته في urls.py الرئيسي, وهذا ما قمت به في المثال أدناه.

    فيما يلي التعديل المطلوب في ملف urls.py

    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import include, handler404 ,handler500 #إستدعاء لدوال مهم
    from myapp import views #استدعاء الفيو التي بداخلها الدوال التي أنشأنها
    
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('app/', include('myapp.urls')),
    ]
    
    #السطور المهمة
    handler404 = views.error_404 #يتم اسناد قيم الخطأ إلي الدالة 404
    handler500 = views.error_500 #نفس الأمر ولكن لدالة 500

    يمكن تجربة تخصيص صفحتي 404 و 500 بطرق مختلفة لكن هذه ربما الأقل تعقيداً, ومن المهم جداً أن تتذكر بأن صفحاتك المخصصة لن تظهر ومشروعك في وضع DEBUG يجب عليك تعطيله هكذا DEBUG = False من ملف الإعدادات كما وضحنا في البداية, وقد يطلب منك إعداد العناوين المسموح لها بزيارة موقعك ALLOWED_HOSTS , على أي حال قم بضبطها إلى

    ALLOWED_HOSTS = ['*']
    

    النتائج ستكون كما يلي,

    Screenshot_423.png.11a30bbea14d02bfe670cd59c450ec44.png

    بالتوفيق.

    • أعجبني 1
  9. بداية تعد session قاموس عادي أو Dictionary يمكنك التعامل مع محتواه علي هذا الأساس , فمثلا للوصول إلى 

    request.session["name"] = "your Name"

    يمكنك تنفيذ 

    print(request.session["name"])

    ولكن بداخل القالب أو template سيتغير الحال قليلا إلى الشكل التالي:

    <h2> {{ request.session.name }} </h2>

    قد تواجه مشكلة عدم ظهور request بشكل عام داخل مشروع Django الخاص بك وهذا وارد في بعض إصدارات Django والسبب عدم التصريح بإستعمال request داخل template ولكن يمكنك حلها من خلال إضافة 

    'django.template.context_processors.request'

    إلي TEMPLATES بملف settings.py الخاص بك لتبدو مثلا هكذا

    settings.py

    TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                .... بالتأكيد هناك خيارات أخري
                'django.template.context_processors.request', السطر الذي نريد اضافته
                'django.contrib.auth.context_processors.auth',
                .....
                ],
            },
        },
    ]

    ثم اعد تجربة

    <h2> {{ request.session.name }} </h2>

     

    • أعجبني 1
  10. افتراضيا تقوم flask بدعم ASCII في الطلبات الخاصة بها وهو المتسبب في ظهور بعض اللغات التي تكتب من اليمين لليسار بالشكل الذي أشرت إليه في سؤالك, لذا إذا كنا نريد استخدم jsonify من flask فكل ما علينا فعله هو تعطيل استخدام ASCII , من خلال 

    app.config['JSON_AS_ASCII'] = False

    ثم إكمال كودك كما تريد علي سبيل المثال ,

    from flask import (Flask, render_template, jsonify)
    
    app = Flask(__name__, template_folder='template')
    app.config['JSON_AS_ASCII'] = False
    
    
    @app.route('/test', methods = ['POST'])
    def test_fun():
        req = 'მოითხოვეთ'
        return jsonify(data=req)
      
    if __name__ == "__main__":
        app.run(debug = True)

    لن تكون هناك مشكلة سواء أضفت u إلي السلسة النصية أو لا فالنتيجة علي اي حال ستكون

    {
      "data": "მოითხოვეთ"
    }

    استخدم PostMan او curl فالنتيجة ذاتها,

    Screenshot_408.png.d6e2ef665488ac2e00513931b073ff0b.png

    curl -X POST http://localhost:5000/test
    
    
    • أعجبني 1
  11. بداية السؤال مشتت لماذا تسلك طريق معقد لشرح ما تريد في حين يمكنك استبدال السؤال بأخر بسيط وهو:

    اقتباس

    كيف أقسم مشروع flask إلى عدة أجزاء أو ملفات

    الإجابة هنا أنت لست بحاجة إلى مشاركته فإذا عدنا إلى الشرط المعروف والذي نكتبه عادة في المشاريع للتأكد من تنفيذ شئ ما أثناء تشغيل ملف بعينه وهو:

    if __name__ == "__main__":
        #app.run(debug = True)

    يتضح لنا أن الملف الرئيسي يظهر دائما تحت المسمى __main__ مما يعني أن بالإمكان استيراد اي شئ من الملف الرئيسي داخل ملفات المشروع الفرعية, علي سبيل المثال سيكون app موجود داخل الملف الرئيسي دائما فيمكننا إستيراده من خلال: 

    from __main__ import app

    ثم تعيد استيراد الملفات من داخل الكود الرئيسي , ولكن بعد الإعلان:

    app = Flask(__name__)

     

    فيما يلي مثال لمشروع يحتوي على ملفين منفصلين عن الملف الرئيسي

    1- error_app.py

    #error_app.py
    from flask import (request, jsonify)
    from __main__ import app
    
    
    app.config['TRAP_HTTP_EXCEPTIONS']=True
    
    def error_Response(code, name):
        shortname = name.lower().replace(' ', '_')
        error = {'error':{}}
        error['error']['title'] = 'App | {0}'.format(name)
        error['error']['page'] = request.url
        error['error']['name'] = name
        error['error']['code'] = code
        return jsonify(error)
    
    @app.errorhandler(Exception)
    def handle_error(e):
        try:
            if e.code < 400:
                return flask.Response.force_type(e, flask.request.environ)
            elif e.code == 404:
                return error_Response("Page Not Found", "The page you're looking for was not found")
            raise e
        except:
            return error_Response("Error", "Something went wrong")

    2- ajax_req.py

    #ajax_req.py
    from flask import (request, jsonify)
    import requests
    from __main__ import app
    
    
    URL = 'https://example.com/test'
    
    @app.route('/from_ajax', methods = ['POST'])
    def ajax_request():
        req = requests.get(URL).text
        return jsonify(data=req)

    3-main.py

    #main.py
    from flask import (Flask, render_template)
    
    
    app = Flask(__name__, template_folder='template')
    
    #يجب إضافة الملفات بعد الاعلان عن الكائن الذي يتم استخدامه بداخلها 
    #اقصد app
    #وجود الإستدعاء قبل ذلك سيؤدي لظهور اخطاء 
    
    import error_app
    import ajax_req
    
    
    @app.route('/')
    def index():
        return render_template('send_from_ajax.html')
    
    
    if __name__ == "__main__":
        app.run(debug = True)

     

    لذا أنت لست بحاجة لأي إجراء أخر للتعامل مع مشروع مترامي الأطراف والذي يمكن أن يحتوي على شجرة ملفات متعددة ,فقط أليات بسيطة تكون كافية.

    بالتوفيق

    • أعجبني 1
  12. يمكنك تحويل جميع الأخطاء داخل flask إلي دالة تتحكم في شكل ومحتوي الرد أو Response ,

    يتم انشاء دالة او Function لهذه المهمة 

    @app.errorhandler(Exception)

    علي شكل استثناء يظهر في حال الدخول إلي عناوين خاطئة أو حدوث مشكلة جوهرية بأي وظيفة داخل مشروعك,

    في البداية ستبني دالة يتم تنفيذها مباشرة عند الاستثناء

    @app.errorhandler(Exception)
    def handle_error(e):
        try:
            if e.code < 400:
                return flask.Response.force_type(e, flask.request.environ)
            elif e.code == 404:
                return error_Response("Page Not Found", "The page you're looking for was not found")
            raise e
        except:
            return error_Response("Error", "Something went wrong")

    ثم دالة اخري تقوم بصياخة الخطأ الذي يتم اظهاره أو ارساله وهي error_Response, يمكنك التحكم في كل ما بها بحسب ما تريد,

    def error_Response(code, name):
        error = {'error':{}}
        error['error']['title'] = 'App | {0}'.format(name)
        error['error']['page'] = request.url
        error['error']['name'] = name
        error['error']['code'] = code
        return jsonify(error)

    ستبدو رسالة الخطأ كما يلي في حال صفحة خاطئة أو url غير موجود,

    Screenshot_405.png.e62f2092eb233016b09ea8bb514ed08a.png

    وفيما يلي شكل الخطأ في حال وجود مشكلة في دالة أو وظيفة ما,

    Screenshot_406.png.d7799ef0ff8616460579ed4b9a785469.png

    لا تنسى إضافة TRAP_HTTP_EXCEPTIONS إلي app.config بالشكل التالي , 

    app.config['TRAP_HTTP_EXCEPTIONS']=True

    سيبدو دمج الدالتين في مشروع حقيقي بالشكل التالي,,


     

    from flask import (Flask, render_template, request, jsonify)
    import requests
    
    URL = 'https://mm.com/test'
    
    app = Flask(__name__, template_folder='template')
    app.config['TRAP_HTTP_EXCEPTIONS']=True
    
    
    def error_Response(code, name):
        shortname = name.lower().replace(' ', '_')
        error = {'error':{}}
        error['error']['title'] = 'App | {0}'.format(name)
        error['error']['page'] = request.url
        error['error']['name'] = name
        error['error']['code'] = code
        return jsonify(error)
    
    @app.errorhandler(Exception)
    def handle_error(e):
        try:
            if e.code < 400:
                return flask.Response.force_type(e, flask.request.environ)
            elif e.code == 404:
                return error_Response("Page Not Found", "The page you're looking for was not found")
            raise e
        except:
            return error_Response("Error", "Something went wrong")
    
    
    @app.route('/')
    def index():
        return render_template('send_from_ajax.html')
            
            
    @app.route('/from_ajax', methods = ['POST'])
    def ajax_request():
        req = requests.get(URL).text
        return jsonify(data=req)
        
        
    if __name__ == "__main__":
        app.run(debug = True)

     

    يمكنك دائما إجراء تغيرات علي دالة error_Response لكي تفسر الخطأ القادم إليك من flask دون الحاجة إلي الوصول لخط التشغيل او command flask ,هذه كانت فقط بذرة,,

    بالتوفيق إن شاء الله

    • أعجبني 1
  13. علامة الزائد أو + ليس لها علاقة بكود أو استعلام sql الذي تم تحويله إلى js ولكن سبب وجودها هو دمج السطر الأول مع السطر الثاني ,فبرمجيا يمكن جمع السلاسل النصية دون مشكلة كما في المثال التالي:

    var text_A = 'one day';
    var text_B = 'from any week';
    let total = text_A + text_B;

    والناتج

    one dayfrom any week

    Screenshot_387.thumb.png.67430b502e326f84c7eab70fc8f40dcc.png

    ولا مشكلة من تكرار ذلك عدة مرات

    Screenshot_388.thumb.png.79f4875e077d92bf088b235ed6d36a9c.png

    أو حتي عدم استخدام var في الاعلام يمكن جمع السلاسل النصية بالشكل التالي:

    Screenshot_390.thumb.png.83d70bb6ee02589d58c8a1442c92f613.png

     

    فالسبب وراء كتابته + أنه اراد تقسيم الاستعلام أو كود sql الطويل نسبيا على عدة سطور, وجعله سهل القراءة دون الحاجة إلي التمرير افقيا مع السطور الطويلة,

    بإختصار ما تقوم به + هي دمج السلسلتين معاً, ويمكنك تقسيمه إلى أكثر من ذلك لاحظ المثال التالي:

    db.all('Comments.flame, Comnents.Content,' + 
           ' rticle.ArticleName,Article.Date' +
           ' from Coments inner join Article' +
           ' on Connents.ArticleId = Article.ArticleId',
           function(err, teble){
              if(err){
                return console.log(err.message);
              }
              console.log(table);
           }
    );

    فقط ركز علي المسافات وتجنب مثلا ان يتم كتابة التالي

    اقتباس

     

    rticle.ArticleName,Article.Datefrom Coments inner join Article

     

    نلاحظ أن Datefrom تم دمجهما في حين أنه يجب وجود مسافة بينهما Date from, لهذا في بداية كل سلسلة تجد أننا نضيف مسافة فارغة داخل علامتي التنصيص ,وبالطبع تختلف الحالات بإختلاف الشئ الذي نريد القيام به.

    Screenshot_391.png.3206ccd7f7db24f3672481befe319a99.png

    وايضا لا فرق إن كتبت السطر كاملا:

    db.all('Comments.flame, Comnents.Content,rticle.ArticleName,Article.Date from Coments inner join Article on Connents.ArticleId = Article.ArticleId',
           function(err, teble){
              if(err){
                return console.log(err.message);
              }
              console.log(table);
           }
    );

     

     

    Screenshot_389.png

    الكود المستخدم في الصور يمكن رؤيته واختباره هنا https://jsfiddle.net/v9oa68r1/

  14. بدلا من ذلك فكر في حل المشكلة المتعلقة ب ajax فكر في اختصار تلك الحلقة وزمن تنفيذها , قد تكون المشكلة متعلقة بنوع البيانات dataType المعلن عنها في طلبك على سبيل المثال يمكنك إرفاق التالي:

    dataType: 'json' 
    //OR
    dataType: 'jsonp' 

    إلى الطلب الخاص بك.

    أيضا تحتاج إلي إضافة CORS لطلبك وربما إلى:

    "Access-Control-Allow-Origin"

    ولا تزال هناك خيارات وحلول أخرى, وبالطبع لا يمكننا معرفة الحل الأمثل بدون رؤية الطلب الذي تقوم بإرساله,والخطأ الذي يواجهك.

    ومع ذلك قد تكون الأخطاء التي تواجهك عن ارسال طلب إلي موقع او خادم آمن https من خلال عنوان غير آمن,, تعد CORS مهمة في هذا الشأن, بإختصار تدعم آلية CORS الطلبات عبر المصدر الآمن ونقل البيانات بين المتصفحات والخوادم.

    أما عن تنفيذ الفكرة التي تفكر بها ,ففيما يلي مثال على تنفيذها,

    1-: send_from_ajax.html

    <!DOCTYPE HTML>
    <html>
    <head>
    <title>Send from AJAX Teo Flaskt</title>
        <script>
            function getData()
            {
                var req = new XMLHttpRequest()
                req.onreadystatechange = function()
                {
                    if (req.readyState == 4)
                    {
                        if (req.status != 200)
                        {
                            //error handling code here
                        }
                        else
                        {
                            var response = JSON.parse(req.responseText)//يمكنك الاستفادة من الرد في اي شئ اخر
                            document.getElementById('testdiv').innerHTML = response.data
                        }
                    }
                }
            
                req.open('POST', '/from_ajax')
                req.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
                req.send('test')
                
                return false
            }
        </script>
    </head>
    <body>
       <h1>Send from AJAX Teo Flask</h1>
       <form action="" method="POST">
           <input type="button" value="Send request" onclick="return getData()">
       </form>
       <h1>Result</h1>
       <div id="testdiv"></div>
    </body>
    </html>

    2-: main.py

    from flask import (Flask, render_template, jsonify)
    import requests
    
    app = Flask(__name__, template_folder='template')
    
    URL = 'https://example.com/test'
    
    @app.route('/')
    def index():
        return render_template('send_from_ajax.html')
            
            
    @app.route('/from_ajax', methods = ['POST'])
    def ajax_request():
        req = requests.get(URL).text
        return jsonify(data=req)
        
        
    if __name__ == "__main__":
        app.run(debug = True)

    وستجد في المرفقات صوراً للنتيجة 

    ويمكن استخدام response الموجودة في طلب ajax في اي عملية اخرى,او اختبار آخر والعكس صحيح اي إرسال url من خلال طلب اجاكس يتم معالجته من خلال flask واعادة ارسال الناتج إلي ajax والذي يتولى مسؤولية عرضه داخل الصفحة الخاصة بنا..

    ومع هذا قد لا تكون طريقة منطقية او مناسبة اللهم في حالات مختلفة تتشابه مع مفاهيم vpn أو بالأحري Proxy, بمعنى أننا قد نحتاج الى إرسال طلب ما إلي خادم او موقع محظور على مستخدمينا الوصول إليه ,فهنا يمكن تثبيت flask على خادم مسموح له بالوصول إلى الموقع أو الخادم المحظور بينما نرسل نحن أو المستخدمين فقط إلى flask ونتلقى النتائج من خلاله ذات الصفحة على المتصفح دون الحاجة إلى proxy وما شابه.

    بالتوفيق

     

    Screenshot_386.png

    InkedScreenshot_384_LI.jpg

    • أعجبني 1
  15. المشكلة تكمن حول الرد الذي يعود بعد تنفيذ requests.post حيث تعيد post كائن من النوع ```requests.models.response``` ,بينما يجب أن يكون الرد النهائي للدالة hi person والتى نخاطبها إما str أي سلسلة نصية او json ,يمكننا اعادة أي منهما ولكن التعديل التالي يعيد string من خلال إضافة .text للرد القادم من post.

    from flask import Flask, url_for, request
    import requests
    
    app = Flask(__name__)
    
    @app.route("/<name>/hello", methods=["POST", "GET"])#يسمح لك بالتجربة من المتصفح GET هذه ليست ضرورية لكن اضافة
    def hi_person(name):
        result = requests.post(url_for("hello", _external=True), data={"name": name})
        return result.text #.text يجب ان يكون الرد اما سلسة نصية أو جسون لهذا أضفنا 
    
    @app.route("/hello", methods=["POST"])
    def hello():
        return 'Hi, ' + request.form["name"]
    
    if __name__ == "__main__":
        app.run(debug=True)

    أما عن سبب إضافة GET فهو إذا أردت تجربة الطلب من خلال المتصفح , ويمكن إجراء الطلب GET و POST من خلال postman أو curl

    GET

    curl -X GET http://localhost:5000/ahmed/hello

    POST

    curl -X POST http://localhost:5000/glal/hello

     

    موفق

    • أعجبني 1
  16. لست مضطرا لتكرار الكتابة اكثر من مرة , فقط فكر بطريقة مختلفة ,فمثلا هنا انت تريد كتابة title ودعني اخمن انك تريد كتابة عنوان واحد ولكن تريد ربما اضافة شرط لعرض عنوان مختلف 

    فمن الممكن اضافة title مرة واحدة فقط بهذا الشكل

    {% block title %}
        {% if user.is_authenticated %}
           <h2> Welcome! {{request.user.username}} To Official Blog</span></h2>
        {%else %}
           <h2> Please login to see the Official Blog</span></h2>
        {% endif %}
    {% endblock %}

     

    • أعجبني 1
  17. اجل يمكنك استدعاء هذه الملفات من خلال السطور التالية في بداية الملف

    {% include "_modal.html" %}

    ويمكنك ايضا اختصار مشروعك لابعد مدي من خلال انشاء base.html تكون بمثابة  header ,footer  والقالب الرئيس لمشروعك, ويمكنك اضافتها في إلي ملفاتك من خلال

    {% extends 'base.html' %}

    سيتم اضافة القالب الرئيسي في الملف الحالي ولن تكون بحاجة الي تضمين وسوم html الاساسية ,ولا header 
    فقط ستكتب <div> الخاص بك

    • أعجبني 1
  18. قم ب الغاء أو حذف request.args['x'] انت اولا لم تقم بعمل import لrequest ولهذا لن يتم تنفيذ الدالة index<

    اذا راجعت log التطبيق اثناء التشغيل ستجد الخطأ التالي,

    NameError: name 'request' is not defined

    أما لماذا لم تظهر لك اي اخطاء سوي الرسالة المرفقة, هو انك لم تقم بتفعيل وضع debug عند تشغيل المشروع ممايعني لن يتم اصدار اي تقارير حول الاخطاء او المشكلات,

    انصحك خلال التعلم ان تقوم بتفعيل الخيار debug دائما ,هكذا

    app.run(debug=True)

     

    • أعجبني 1
  19. اضف المحازاة الي المنتصف من خلال كتابة السطر التالي

    align-items: center;

    في ال class الذي تستخدمه وذلك بداخل css الخاص بمشروعك

    .center {
        display: flex;
        justify-content: center;
        align-items: center;
        height:100vh;  
    }

    أو قم بإضافته مباشرة إلي div الرئيسية, أو اضفها الي body ,هكذا 

    <div style="align-items: center;"> .... </div>

    بعض الخيارات الاخري متاحة ولكن ان لم تعرف كيف تكتب ,يمكنك عرض جزء من الكود وسنعيد صياغته,موفق

  20. استخدم lambda x: x.first_name ك key داخل دالة sorted ,

    sorted(authors, key=lambda x: x.first_name)

    ويمكنك عكس الترتيب بإضافة reverse=True

    وهناك حل اخر ولكنه تعديل بسيط علي الاستعلام الخاص بك هذا

    authors = Author.objects.order_by('-score')[:30]

    يمكنك اضافة جملة اخري للاستعلام ولكن انتبه ان اردت استخدام - لانها تعيد الترتيب تنازليا ,فقط اضف التالي للبحث الخاص بك,.
     

    ('-score', 'first_name')[:30]

     

    • أعجبني 1
  21. يمكن البحث او الفلترة بسهولة من خلال المفتاح او الحقول التي تلي ForeignKey لانه فرع حقيقي داخل الجدول الرئيسي Image مثلا.

    فقط استخدم contains بعد اسم الجدول الفرعي واسم الحقل الذي تبحث بداخله مفصولة ب __

    filter(ForeignKeyName__fieldName__contains="project 1")

     

    • أعجبني 1
×
×
  • أضف...