رفتن به مطلب

Mohammad Aref

مدیر ارشد
  • تعداد ارسال ها

    33,525
  • تاریخ عضویت

  • آخرین بازدید

  • روز های برد

    430

تمامی مطالب نوشته شده توسط Mohammad Aref

  1. جزوه بعدی هم به صورت پاورپوینت هست که توسط حسين عبده تبريزي در 7 بهمن 1395 برای چهارمین کنفرانس ملی پژوهش‌های کاربردی در مهندسی عمران، معماری و مدیریت شهری و سومین کنفرانس و نمایشگاه تخصصی انبوه‌سازان مسکن و ساختمان استان تهران در 42 اسلاید با عنوان سیمای اقتصاد مسکن و راهکارهای بهبود آن تهیه شده است که از لینک زیر می توانید دانلود کنید. دانلود جزوه
  2. اینم یه پاورپوینت دیگه هستش با عنوان تحلیل استراتژیک بازار مسکن (pest & swot) که توسط بهاره صبور در 32 اسلاید تهیه شده و از پیوست می تونید دانلود کنید. تحلیل استراتژیک بازار مسکن.ppt
  3. Mohammad Aref

    آموزش جنگو Django Framework

    وارد و خارج شدن از سایت جنگو برای کنترل ورود و خروج از سایت، برخی توابع داخلی (و چند فوت و فن جذاب) را ارائه کرده است، ولی قبل از آن، اجازه دهید نگاهی به نحوه ی ورود و خروج از سایت را "به صورت دستی" بیاندازیم. جنگو جهت انجام این اعمال در django.contrib.auth دو تابع با نام های authenticate() و login() را ارائه کرده است. جهت تصدیق یک نام کاربری و رمز عبور داده شده، از authenticate() استفاده کنید. این تابع دو آرگومان کیورد username و password را دریافت می کند، و در صورتی که رمز عبور برای نام کاربری داده شده معتبر باشد، یک شیء User بر می گرداند. در صورتی که رمز عبور معتبر نباشد، authenticate() مقدار None بر می گرداند: >>> from django.contrib import auth >>> user = auth.authenticate(username='john', password='secret') >>> if user is not None: ... print "Correct!" ... else: ... print "Invalid password." authenticate() تنها اعتبار کاربر را تایید می کند. جهت ورود کاربر، از تابع loging() استفاده کنید. این تابع یک شیء HttpRequest و یک شیء User دریافت کرده و با استفاده از فریم ورک session، ID کاربر را درون session ذخیره می کند. مثال زیر نحوه ی استفاده از هر دوی تابع authenticate() و login() را درون یک تابع view نشان می دهد: from django.contrib import auth def login_view(request): username = request.POST.get('username', '') password = request.POST.get('password', '') user = auth.authenticate(username=username, password=password) if user is not None and user.is_active: # Correct password, and the user is marked "active" auth.login(request, user) # Redirect to a success page. return HttpResponseRedirect("/account/loggedin/") else: # Show an error page return HttpResponseRedirect("/account/invalid/") جهت خروج یک کاربر، از django.contrib.auth.logout() داخل view خود استفاده کنید. این تابع یک شیء HttpRequest دریافت کرده و هیچ مقداری بر نمی گرداند: from django.contrib import auth def logout_view(request): auth.logout(request) # Redirect to a success page. return HttpResponseRedirect("/account/loggedout/") دقت داشته باشید که auth.logout() در صورتی که کاربر وارد سایت نشده باشد، هیچ خطایی ایجاد نمی کند. در عمل، نیازی به نوشتن توابع login/logout خودتان نخواهید داشت؛ سیستم تصدیق مجموعه ای از view ها برای کنترل ورود و خروج به طور عمومی ارائه کرده است. اولین گام در استفاده از این view های تصدیق، وصل کردن آن ها به URLconf می باشد. نیاز است تکه کد زیر را اضافه کنید: from django.contrib.auth.views import login, logout urlpatterns = patterns('', # existing patterns here... (r'^accounts/login/$', login), (r'^accounts/logout/$', logout), ) /accounts/login/ و /accounts/logout/، URL های پیشفرض می باشند که جنگو برای این view استفاده می کند. به طور پیشفرض view، login یک template را در registeration/login.html، render می کند (می توانید نام این template را از طریق ارسال یک آرگومان view اضافه تغییر دهید، ``template_name``). این فرم نیاز دارد حاوی یک فیلد username و یک فیلد password باشد. یک template ساده ممکن است چیزی شبیه به کد زیر باشد: {% extends "base.html" %} {% block content %} {% if form.errors %} <p class="error">Sorry, that's not a valid username or password</p> {% endif %} <form action="" method="post"> <label for="username">User name:</label> <input type="text" name="username" value="" id="username"> <label for="password">Password:</label> <input type="password" name="password" value="" id="password"> <input type="submit" value="login"> <input type="hidden" name="next" value="{{ next|escape }}"> </form> {% endblock %} در صورتی که وارد شدن موفقیت آمیز باشد، کاربر به طور پیشفرض به /accounts/profile/ تغییر مسیر داده خواهد شد. می توان این حالت را توسط یک فیلد hidden به نام next با URL جهت تغییر مسیر بعد از ورود override کرد. همچنین می تواند این مقدار را به صورت یک پارامتر GET به view، login ارسال کرده و آن به صورت خودکار به صورت متغیر next به context اضافه خواهد شد که می توانید درون آن فیلد hidden آن را درج کنید. view، logout کمی متفاوت تر عمل می کند. به طور پیشفرض این view یک template در registration/loggedout.html (که معمولا حاوی یک پیام "you've successfully logged out" می باشد) را render می کند. می توان view را با یک آرگومان اضافه با نام next_page فراخوانی کرد، که view را جهت تغییر مسیر بعد از خروج راهنمایی خواهد کرد. محدودیت دسترسی برای کاربران وارد شده به طور ساده، راه خام جهت محدود کردن دسترسی برای صفحات، بررسی request.user.is_authenticated() و تغییر مسیر به یک صفحه ی ورود می باشد: from django.http import HttpResponseRedirect def my_view(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/accounts/login/?next=%s' % request.path) # ... یا شاید نمایش یک پیام خطا: def my_view(request): if not request.user.is_authenticated(): return render_to_response('myapp/login_error.html') # ... به صورت یک میانبر، می توان از decorator مناسب login_required استفاده کرد: from django.contrib.auth.decorators import login_required @login_required def my_view(request): # ... login_required به شکل زیر عمل می کند: در صورتی که کاربر وارد نشده باشد، به /accounts/login/ تغییر مسیر داده می شود، ارسال شدن مسیر URL فعلی در رشته ی کوئری به صورت next، برای مثال: /accounts/login/?next=/polls/3/. در صورتی که کاربر وارد شده باشد، view به صورت معمول اجرا می شود. کد view می تواند. محدودیت دسترسی برای کاربرانی که یک آزمون را رد می کنند محدودیت دسترسی بر پایه ی حق دسترسی ها یا برخی آزمون های دیگر، یا ارائه ی یک مکان مختلف برای view ورود اساسا به یک روش کار می کند. روش خام اجرای آزمون در request.user به طور مستقیم درون view می باشد. برای مثال، view زیر برای اطمینان از این که کاربر، وارد شده و دارای حق دسترسی polls.can_vote می باشد یا خیر: def vote(request): if request.user.is_authenticated() and request.user.has_perm('polls.can_vote')): # vote here else: return HttpResponse("You can't vote in this poll.") بار دیگر، جنگو یک میانبر با نام user_passes_test ارائه کرده است. این میانبر آرگومان هایی دریافت کرده و یک decorator تخصص یافته برای وضعیت خاص شما تولید می کند: def user_can_vote(user): return user.is_authenticated() and user.has_perm("polls.can_vote") @user_passes_test(user_can_vote, login_url="/login/") def vote(request): # Code here can assume a logged-in user with the correct permission. ... user_passes_test یک آرگومان الزامی دریافت می کند: یک قابل فراخوانی که یک شیء User دریافت کرده و در صورتی که کاربر اجازه ی تماشای صفحه را داشته باشد مقدار True بر می گرداند. توجه داشته باشید که user_passes_test به طور اتوماتیک تصدیق شدن کاربر را بررسی نمی کند؛ شما باید آن را برای خودتان انجام دهید. همچنین در این مثال آرگومان دوم (اختیاری) نشان داده شده است، که اجازه ی تعیین URL برای صفحه ی خودتان را می دهد (/accounts/login/ به طور پیشفرض). در صورتی که کاربر آزمون را نگذرانده باشد؛ سپس decorator، user_passes_test کاربر را به login_url تغییر مسیر خواهد داد. به دلیل آن که بررسی این که یک کاربر دارای یک حق دسترسی خاص است یا خیر یک وظیفه ی نسبتا مشترک می باشد، جنگو یک میانبر برای آن ارائه کرده است که یک decorator با نام permission_required() می باشد. from django.contrib.auth.decorators import permission_required @permission_required('polls.can_vote', login_url="/login/") def vote(request): # ... دقت داشته باشد که همچنین permission_required() یک پارامتر اختیاری login_url دریافت می کند؛ که این نیز به طور پیشفرض '/accounts/login/' می باشد. محدود کردن دسترسی برای view های جنریک یکی از سوالات تکراری پرسیده شده در لیست کاربران جنگو در مورد محدود کردن دسترسی برای یک view جنریک می باشد. برای جواب به این سوال؛ نیاز به نوشتن یک thin wrapper در اطراف view و اشاره URLconf به wrapper خود به جای خود generic view خواهید داشت: from django.contrib.auth.decorators import login_required from django.views.generic.date_based import object_detail @login_required def limited_object_detail(*args, **kwargs): return object_detail(*args, **kwargs) البته که می توانید، login_required را با هر decorator محدودیت دیگری جا به جا کنید. مدیریت کاربران، حق دسترسی ها و گروه ها ساده ترین را برای مدیریت سیستم auth تاکنون، از طریق رابط مدیر بوده است. سایت مدیر در مورد نحوه ی استفاده از سایت مدیر جنگو را جهت ویرایش کاربران و کنترل حق دسترسی آن ها بحث کرده است، و اغلب اوقات شما فقط از این رابط استفاده خواهید کرد. هر چند API های سطح پایین ای وجود دارند که شما هنگامی که نیاز به کنترل مستقل دارید از آن ها استفاده کنید، و این API ها را در بخش های بعدی توضیح داده ایم. ساختن کاربران ساختن کاربران با تابع کمکی create_user: >>> from django.contrib.auth.models import User >>> user = User.objects.create_user(username='john', ... email='jlennon@beatles.com', ... password='glass onion') در این نقطه، user یک رابط نمونه ی User حاضر برای ذخیره شدن در پایگاه داده (create_user() در واقع save() خودش را فراخوانی نمی کند) می باشد. همچنین می توانید قبل از ذخیره attribute های آن را تغییر دهید: >>> user.is_staff = True >>> user.save() تغییر رمزهای عبور می توان یک رمز عبور را با set_password() تغییر داد: >>> user = User.objects.get(username='john') >>> user.set_password('goo goo goo joob') >>> user.save() attribute، password را به طور مستقیم قرار ندهید، مگر اینکه کاملا بدانید که چه کار می کنید. رمز عبور در واقع به صورت یک salted hash دخیره شده و در نتیجه نمی تواند به طور مستقیم ویرایش شود. به طور رسمی تر، attribute، password از یک شیء User یک رشته در این قالب بندی می باشد: hashtype$salt$hash آن یک نوع hash، salt و خود hash، جدا شده توسط حرف ($) می باشد. hashtype همچنین sha1 (پیشفرض) یا md5 می باشد، الگوریتم استفاده شده برای انجام یک hash یک طرفه از رمز عبور. salt یک رشته ی تصادفی استفاده شده برای اضافه شدن به رمز عبور خام برای ساختن hash برای مثال: sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4 توابع User.set.password() و User.check_password() این مقادیر را در پشت صحنه بررسی کرده و قرار می دهند. hash های اضافه شده hash تابع یک طرفه ی پنهانی می باشد – بدین معنی که، می توان به سادگی hash مقدار داده شده را محاسبه کرد، ولی گرفتن یک hash و برگرداندن آن به مقدار اصلی آن تقریبا غیر ممکن است. در صورتی که رمزهای عبور را به صورت متن ساده ذخیره کرده باشیم، هر کسی که درون پایگاه داده ی دست داشته باشد به سرعت می تواند رمز عبور همه را بفهمد. ذخیره رمزهای عبور به صورت hash ها احتمال به خطر افتادن اطلاعات پایگاه داده را کاهش می دهد. هر چند که، یک حمله کنند با رمز عبور پایگاه داده همچنان می تواند یک حمله ی brute-force را اجرا کرده و میلیون ها رمز عبور را به صورت hash در آورده و آن hash ها را با مقادیر ذخیره شده مقایسه کند. این مدتی طول می کشد ولی کمتر آن که شما ممکن است فکر کنید. بدتر از آن rainbow table ها می باشند که به صورت عمومی در دسترس هستند، یا پایگاه های داده ی قبل از محاسبه ی hash ها از میلیون ها رمز عبور. با یک rainbow table، یک مهاجم با تجربه می تواند در چند ثانیه تمام رمزهای عبور را بشکند. اضافه کردن یک salt – اساسا یک مقدار اولیه تصادفی – برای hash ذخیره شده یک لایه ی دیگری را جهت سختی در شکستن رمزهای عبور اضافه می کند. زیرا salt ها در هر رمزعبوری متفاوت می باشند، آن ها همچنین از استفاده ی rainbow table جلوگیری می کنند؛ در نتیجه مهاجمان مجبور به سقوط در یک حمله ی brute-force می شوند، در دست ترجمه/تالیف .... هنگامی hash های salt شده امن ترین روش برای ذخیره رمزهای عبور نیستند، یک حد وسط بین امنیت و راحتی می باشند. کنترل عضویت می توان از این ابزار سطح پایین برای ساختن view هایی که به کاربر جهت عضو شدن برای حساب های جدید اجازه می دهند استفاده کرد. توسعه دهندگان مختلف عضویت را به طور متفاوتی انجام می دهند، بنابراین جنگو نوشتن یک view را برای شما ترک کرده است. خوشبختانه، این کار بسیار ساده می بشاد. در ساده طرین حالت، می توان یک view کوچک برای اطلاعات الزامی کاربر و ساختن آن کاربران تهیه کرد. جنگو یک فرم داخلی ارائه کرده است که می توان برای این منظور از آن استفاده کرد، که در مثال زیر استفاده خواهیم کرد: from django import forms from django.contrib.auth.forms import UserCreationForm from django.http import HttpResponseRedirect from django.shortcuts import render_to_response def register(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): new_user = form.save() return HttpResponseRedirect("/books/") else: form = UserCreationForm() return render_to_response("registration/register.html", { 'form': form, }) این فرم یک template با نام registration/register.html را فرض می کند. در اینجا یک مثال از template مورد نظر وجود دارد: {% extends "base.html" %} {% block title %}Create an account{% endblock %} {% block content %} <h1>Create an account</h1> <form action="" method="post"> {{ form.as_p }} <input type="submit" value="Create the account"> </form> {% endblock %} استفاده از تصدیق داده در Template ها کاربر وارد شده ی فعلی و حق دسترسی های وی، زمانی که از RequestContext (آموزش template پیشرفته را نگاه کنید) استفاده می کنید در template context در دسترس می باشد. نکته از نظر فنی، این متغیرها تنها در صورتی که شما از RequestContext استفاده کنید و تنظیم TEMPLATE_CONTEXT_PROCESSORS حاوی "django.core.context_processors.auth" باشد (که به طور پیشفرض این طور است) در template context در دسترس می باشند. بار دیگر برای اطلاعات بیشتر می توانید به آموزش template پیشرفته مراجعه کنید. هنگامی که از RequestContext استفاده می کنید، کاربر فعلی (که می تواند هم یک نمونه از User یا یک نمونه ی AnonymousUser باشد) در متغیر template {{ user }} ذخیره می شود: {% if user.is_authenticated %} <p>Welcome, {{ user.username }}. Thanks for logging in.</p> {% else %} <p>Welcome, new user. Please log in.</p> {% endif %} حق دسترسی های این کاربر در متغیر template {{ perms }} ذخیره شده اند. این یک پروکسی template‑friendly برای تعدادی از متدهای حق دسترسی می باشد که به طور خلاصه توضیح داده شده است. برای استفاده از شیء perms دو روش وجود دارد. می توان در صورتی که کاربر دارای هیچ حق دسترسی برای برخی برنامه های داده شده نداشته باشد، برای بررسی آن از چیزی شبیه به این {% if perms.polls %} استفاده کرد، یا می توان در صورتی که کاربر دارای حق دسترسی خاصی می باشد برای بررسی آن از چیزی شبیه به این {% if perms.polls.can_vote %} استفاده کرد. در نتیجه، می توان حق دسترسی ها را در عبارت template {% if %} بررسی کرد: {% if perms.polls %} <p>You have permission to do something in the polls app.</p> {% if perms.polls.can_vote %} <p>You can vote!</p> {% endif %} {% else %} <p>You don't have permission to do anything in the polls app.</p> {% endif %} حق دسترسی ها، گروه ها و پیام ها چند قسمت دیگر از فریم ورک authentication وجود دارد که تنها به طور روزنامه وار از کنار آن عبور کردیم. در ادامه نگاهی نزدیک تر به آن ها خواهیم داشت. حق دسترسی ها حق دسترسی ها روشی ساده برای "علامت گذاری" کاربران و گروه ها جهت نشان دادن این که کاربر یا گروه علامت گذاری شده دارای توانایی اجرای برخی کارها می باشد. حق دسترسی ها معمولا توسط سایت مدیر جنگو استفاده شده اند، ولی می توان به سادگی در کد خود نیز از آن ها استفاده کرد. سایت مدیر جنگو به صورتی که در زیر بیان شده است از حق دسترسی ها استفاده کرده است: دسترسی به view فرم "add"، و اضافه کردن یک شیء که محدود به کاربران با حق دسترسی add برای آن نوع از شیء. دسترسی به view لیست تغییر، view فرم "change"، و تغییر یک شیء که به کاربران با حق دسترسی change برای آن نوع شیء محدود شده است. دسترسی به حذف یک شیء محدود شده به کاربران با حق دسترسی delete برای آن نوع از شیء. حق دسترسی ها به صورت globally برای هر نوع از آجکت، نه هر نمونه ی خاص از شیء قرار داده شده اند. برای مثال، می توان گفت "Mary قادر است اخبار را تغییر دهد" ولی حق دسترسی ها اجازه نمی دهد که به عنوان مثال بگویید "Mary قادر است اخبار تغییر دهید، ولی تنها آنهایی را که خود او ساخته است" یا "Mary قادر است تنها اخباری را تغییر دهید که دارای یک وضعیت خاصی، انتشار یا ID خاص باشد." این ها سه حق دسترسی اساسی می باشد – اضافه کردن، تغییر دادن، و حذف کردن – که به طور خودکار برای هر مدل جنگو ایجاد شده اند. در پشت صحنه، این حق دسترسی ها هنگامی که شما دستور manage.py sycdb را اجرا می کنید درون جدول پایگاه داده با نام auth_permission اضافه می شوند. این حق دسترسی ها فرمی از "._" می باشند. بدین معنی که اگر دارای یک برنامه ی polls (نظرسنجی) با یک مدل Choice می باشید، حق دسترسی های "polls.add_choice"، "polls.change_choice" و "polls.delete_choice" را بدست خواهید آورد. درست مثل کاربران، حق دسترسی ها در یک مدل جنگو موجود در django.contrib.auth.models اجرا شده اند. این یعنی این که می توان در صورت تمایل ارتباط با permission ها، به طور مستقیم از API پایگاه داده ی جنگو استفاده کرد. گروه ها گروه ها یک view جنریک از طبقه بندی کاربران می باشد، بنابراین می توان حق دسترسی ها، یا برخی چیزهای دگر را برای آن کاربران بکار برد. یک کاربر می تواند به هر تعدادی از گروه ها تعلق داشته باشد. یک کاربر در یک گروه به طور خودکار دارای حق دسترسی های داده به آن گروه می باشد. برای مثال، در صورتی که گروه Site editors دارای حق دسترسی can_edit_home_page باشد، هر کاربر در آن گروه آن حق دسترسی را خواهد داشت. گروه ها همچنین روش مناسبی برای طبقه بندی کاربران برای دادن برخی لیبل ها یا قابلیت تمدید به آن ها می باشد. برای مثال، می توان یک گروه با نام 'Special users' ایجاد نمود، و آن قبیل از کاربرانی را که می خواهیم در یک بخش از سایت که تنها برای دسترسی کاربران در نظر گرفته شده است فعالیت کنند را در آن قرار دهیم، یا پیام هایی که تنها برای کاربران در نظر گرفته ایم را به آن ها ارسال کنیم. همانند کاربران، ساده ترین روش برای مدیریت گروه ها، از طریق رابط مدیر می باشد. هر چند، گروه ها نیز تنها مدل های جنگو می باشند که در django.contrib.auth.models وجود دارند. بنابراین شما می توانید همواره برای سرو کار داشتن با گروه ها در سطح پایین از API پایگاه داده ی جنگو استفاده کنید. پیام ها سیستم پیام یک روش سبک جهت به صف کردن پیام برای کاربران داده شده می باشد. یک پیام، مرتبط با یک User می باشد. هیچ مفهومی از انقضا یا برچسب زمانی وجودد ندارد. پیام ها توسط رابط مدیر جنگو بعد از اعمال موفقیت آمیز استفاده می شوند. زمانی که شما یک آجکت ایجاد می کند، متوجه ی یک پیام "The object was created successfully" در بالای صفحه ی مدیر خواهید شد. می توانید از یک API هم شکل برای صف بندی و نمایش پیام ها در برنامه ی خودتان استفاده کنید. API ساده می باشد: جهت ایجاد یک پیام جدید، از user.message_set.create(message='message_text') استفاده کنید. جهت بازیابی/حدف پیام ها، از user.get_and_delete_messages() استفاده کنید، که یک لیست از شیء های Message در صف کاربران (در صورت وجود) بر می گرداند و پیام های از صف را حذف می کند. در مثال view زیر، سیستم یک پیام برای کاربر بعد از ساختن یک playlist ذخیره می کند: def create_playlist(request, songs): # Create the playlist with the given songs. # ... request.user.message_set.create( message="Your playlist was added successfully." ) return render_to_response("playlists/create.html", context_instance=RequestContext(request)) هنگامی که شما از RequestContext استفاده می کند، کاربر وارد شده ی فعلی و پیام او در template context به صورت متغیر template {{ message }} در دسترس هستند. در زیر یک مثال از کد template وجود دارد که پیام ها را نمایش می دهد: {% if messages %} <ul> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} دقت داشته باشید که RequestContext در پشت صحنه get_and_delete_messages را فراخوانی می کند، بنابراین هر پیامی حذف شده خواهد بود حتی اگر آن ها را نمایش ندهید. در پایان، دقت داشته باشید که این فریم ورک پیام ها تنها با کاربران در پایگاه داده ی کاربر کار می کنند. برای ارسال پیام ها به کاربران anonymous، از چارچوب session به طور مستقیم استفاده کنید.
  4. Mohammad Aref

    آموزش جنگو Django Framework

    Session ها، کاربران، و عضویت مرورگر هایی که سایت های ما را مورد هدف قرار می دهند که در پشت خود انسان های واقعی دارند (حداقل بیشتر اوقات). این نکته ی بزرگی برای رد کردن است: بهترین حالت اینترنت زمانی می باشد که برای وصل شدن به مردم خدمات می دهد، نه ماشین ها. در صورتی که بخواهیم به درستی سایت ها را وادار کنیم، سر انجام باید با افراد در پشت مرورگر ها سر و کار داشته باشیم. متاسفانه، این موضوع ساده ای نمی باشد. HTTP طوری طراحی شده است که بی حالت باشد – بدین معنی که، هر درخواست در یک فضای تهی اتفاق می افتد. دوامی بین یک درخواست و درخواست بعدی وجود ندارد، و ما نمی توانیم هر یک از جنبه ها درخواست (آدرس IP، مرورگر و غیره ...) را که به طور مداوم توسط یک شخص به طور پی در پی ارسال می شود را شمارش کنیم. در این آموزش شما نحوه ی کنترل این فقدان حالت را خواهید آموخت. با پایین ترین سطح (کوکی ها) شروع خواهیم کرد، و به سمت ابزار سطح بالاتر برای کنترل session ها، کاربران و عضویت حرکت خواهیم کرد. کوکی ها (Cookies) توسعه دهندگان مرورگر مدت ها پیش متوجه این موضوع شدند که وضعیت statelessness یک مشکل بزرگ برای توسعه دهندگان وب به شمار می رود، و در نتیجه کوکی ها چشم به جهان گشودند. کوکی یک تکه ی کوچک از اطلاعات می باشد که مرورگرها از طرف وب سرورها ذخیره می کنند. هر بار درخواست های یک مرورگر از یک صفحه ی فرم و از یک سرور خاص، به کوکی که در ابتدا دریافت شده است پس داده می شوند. اجازه دهید نگاهی به نحوه ای که این عمل ممکن است انجام شود بیاندازیم. هنگامی که شما مرورگر خود را باز می کنید و درون آن google.com را تایپ می کنید، مرورگر شما یک درخواست HTTP را به گوگل می فرستد که با چیزی شبیه به این شروع می شود: GET / HTTP/1.1 Host: google.com ... زمانی که گوگل پاسخ می دهد، یک پاسخ HTTP شبیه به پاسخ زیر خواهد بود: HTTP/1.1 200 OK Content-Type: text/html Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com Server: GWS/2.1 ... به هدر Set_Cookie دقت کنید. مرورگر شما مقدار کوکی که از این قرار است ذخیره خواهد کرد (PREF=ID=5b14f22bdafle81c:TM=1167000671:LM=1167000671) و در هر بار که شما به سایت دسترسی پیدا کنید آن را به گوگل بر می گرداند. بنابراین در مرتبه ی بعدی که به گوگل دسترسی پیدا می کنید، مرورگر شما یک درخواست مانند زیر را ارسال خواهد کرد: GET / HTTP/1.1 Host: google.com Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671 ... سپس گوگل می تواند از آن مقدار کوکی برای دانستن این که شما با کسی که قبل تر دسترسی پیدا کرده است یک فرد همسان می باشید استفاده می کند. این مقدار ممکن است برای مثال، یک کلید درون یک پایگاه داده که اطلاعات کاربر را ذخیره می کند باشد. گوگل می تواند (انجام می دهد) از آن برای نمایش نام کاربری حساب شما در صفحه استفاده کند. قرار دادن و گرفتن کوکی ها هنگامی که در جنگو با ماندگاری سر و کار دارید، بیشتر اوقات از seesion سطح بالا و یا از فریم ورک یا چارچوب های کاربر بحث شده در این آموزش از کتاب، استفاده خواهید کرد. با این حال، ابتدا به نحوه ی خواندن و نوشتن کوکی ها در سطح پایین نگاهی بیاندازید. این باید به فهم بقیه ی ابزار بحث شده در آموزش و در صورتی که شما بخواهید به صورت مستقیم از کوکی ها استفاده کنید به شما کمک خواهد کرد. خواندن کوکی ها ساده می باشد. هر شیء HttpRequest دارای یک شیء COOKIES می باشد که مانند دیکشنری عمل می کند؛ می توانید برای خواند هر کوکی که مرورگر به view ارسال می کند از آن استفاده کنید: def show_color(request): if "favorite_color" in request.COOKIES: return HttpResponse("Your favorite color is %s" % \ request.COOKIES["favorite_color"]) else: return HttpResponse("You don't have a favorite color.") نوشتن کوکی ها قدری پیچیده تر می باشد. نیاز است که از متد set_cookie() در یک شیء HttpResponse استفاده کنید. در زیر مثالی وجود دارد که کوکی favorite_color مستقر در یک پارامتر GET را قرار می دهد: def set_color(request): if "favorite_color" in request.GET: # Create an HttpResponse object... response = HttpResponse("Your favorite color is now %s" % \ request.GET["favorite_color"]) # ... and set a cookie on the response response.set_cookie("favorite_color", request.GET["favorite_color"]) return response else: return HttpResponse("You didn't give a favorite color.") می توانید همچنین یک تعداد از آرگومان های اختیاری را به response.set_cookie() که جنبه هایی از کوکی را کنترل می کند ارسال کنید. همانطور که در جدول زیر مشاهده می کنید. پارامتر پایگاه داده توضیح Max_age None عمر (بر حسب ثانیه) که کوکی باید زنده باشد. در صورتی که این پارامتر None باشد، کوکی تنها تا زمانی که مرورگر بسته شود زنده خواهد ماند. expire None زمان و تاریخ واقعی ای، که کوکی باید از بین برود که باید در قالب بندی "Wdy, DD-Mth-YY HH:MM:SS GMT" باشد. در صورت داده این پارامتر، پارامتر max_age باز نویسی می شود. path "/" پیشوند مسیری که کوکی برای آن معتبر است. مرورگرها کوکی را تنها به صفحاتی که تحت این مسیر پیشوند می باشد بر می گرداند، بنابراین شما می توانید از این برای جلوگیری از ارسال کوکی ها به بخش های دیگر سایت استفاده کنید. این پارامتر به ویژه هنگامی که سطح زیادی زا دامنه های سایت را کنترل نمی کنید مفید است. domain None دامنه ای که این کوکی برای آن معتبر است. می توانید از این پارامتر برای قرار دادن یک کوکی cross-domain استفاده کنید. برای مثال، domain=".example.com" یک کوکی قرار خواهد داد که توسط دامنه های www.example.com، www2.example.com و sub.domain.example.com قابل خواندن می باشد. در صورتی که این پارامتر None قرار داده شود، یک کوکی تنها توسط دامنه ای که آن را قرار داده قابل خواندن خواهد بود. secure False در صورتی که مقدار این پارامتر را True قرار داده شود، این پارامتر به مرورگر خواهد گفت که این کوکی را تنها برای صفحاتی که HTTPS می باشند در دسترس قرار بده. The Mixed Blessing of Cookies ممکن است دقت کرده باشید که یک تعداد از مشکلات بالقوه روش کار با کوکی ها وجود دارند. اجازه دهید برخی از این مشکلات را مورد بررسی قرار دهیم: ذخیره سازی کوکی ها ارادی می باشد؛ یک کلاینت ملزم به قبول یا ذخیره کوکی نمی باشد. در واقع، تمام مرورگرها کاربران را قادر به کنترل سیاست خود برای قبول کردن کوکی ها می کند در صورتی که می خواهید تنها کوکی های واجبی که در وب وجود دارند را تماشا کنید، option مرورگر خود یعنی "prompt to accept every cookie" را روشن کنید. با وجود تقریبا استفاده ی جهانی آنها، کوکی ها هنوز غیر قابل اطمینان تلقی می شوند. این بدان معنی است که توسعه دهندگان باید بررسی کنند که یک کاربر در واقع کوکی ها را قبل اعتماد به آن ها قبول کرده است. کوکی ها (به ویژه آن هایی که در HTTPS ارسال نمی شوند) امن نمی باشند. چرا که داده ی HTTP به صورت متن ساده ارسال می شود، کوکی ها به شدت در برابر حملات جاسوسی آسیب پذیر می باشند. این بدین معنی است که، یک مهاجم جاسوس می تواند یک کوکی را قطع کرده و آن را بخواند. این بدان معنی است که نباید هرگز اطلاعات حساس را در یک کوکی ذخیره کرد. حتی یک حمله ی مخرب تر که بک حمله ی man-in-the-middle معروف است وجود دارد، در جایی که یک مهاجم یک کوکی را قطع می کند و از برای نشان دادن خود به صورت یک کاربر دیگر استفاده می کند. آموزش امنیت به تفصیل این مسائل و راه ها جلوگیری از آن را توضیح داده است. کوکی ها حتی از دریافت کنندگان در نظر گرفته ی خود نیز امن نمی باشد. اغلب مرورگر ها روشی ساده برای ویرایش محتوای کوکی های منحصر به فرد تهیه می کنند، و کاربران خبره می توانند همواره با استفاده از ابزاری مانند mechanize ([Hidden Content]) درخواست های HTTP به صورت دستی بسازند. بنابراین شما نمی توانید داده ی درون کوکی ها را که برای دستکاری حساس می باشند را ذخیره کنید. اشتباه رایج در این سناریو ذخیره چیزی شبیه به IsLoggedIn=1 در یک کوکی در هنگامی که کاربر وارد شده است می باشد. متعجب می شوید وقتی تعدادی از سایت ها این اشتباه طبیعی را انجام می دهند؛ فریب دادن سیستم های امنیتی این سایت ها تنها یک ثانیه طول می کشد. فریم ورک یا چارچوب Session جنگو با تمام این محدودیت ها و حفره های امنیتی بالقوه، واضح است، آن کوکی ها و session های مقاوم مثال هایی از نقطه های درد در توسعه ی وب می باشند. البته، هدف جنگو داروی مسکن موثر بودن می باشد، بنابراین جنگو حاوی یک فریم روک session طراحی شده جهت تسکین دادن این سختی ها برای شما می باشد. این چارچوب session به شما اجازه می دهد داده ی دلخواه را بر اساس هر بازدید کننده ی سایت ذخیره و بازیابی کنید. این فریم ورک داده را در سمت سرور ذخیره می کند و ارسال و دریافت کوکی ها را حذف می کند. کوکی ها تنها از یک ID هش شده استفاده می کنند – نه خود داده – در نتیجه شما را از مشکلات رایج کوکی محافظت می کند. اجازه دهید به نحوه ی فعال کردن session ها و استفاده کردن از آن ها در view ها بپردازیم. فعال کردن Session ها session ها از طریق قسمتی از middleware (به فرم مراجعه کنید) و مدل جنگو اجرا می شوند. جهت فعال کردن session ها، نیاز است مراحل زیر را انجام دهید: تنظیم MIDDLEWARE_CLASSES را ویرایش کرده و اطمینان حاصل کنید که MIDDLEWARE_CLASSES حاوی 'django.contrib.sessions.middleware.SessionMiddleware'می باشد. اطمینان حاصل کنید که 'django.contrib.sessions' درون تنظیم INSTALLED_APPS وجود دارد (و در صورتیکه ملزم به اضافه کردن آن هستید دستور manage.py syncdb را اجرا کنید). اسکلت بندی پیشفرض تنظیمات ایجاد توسط startproject دارای هر دوی این قسمت ها فوق می باشد، بنابراین در صورتی که آن ها را حذف نکرده باشید، برای کار کردن session ها نیازی به تغییر چیزی نیست. در صورتی که نمی خواهید از session ها استفاده کنید، ممکن است بخواهید خط SessionMiddleware را از MIDDLEWARE_CLASSES و 'django.contrib.sessions'از INSTALLED_APPS خود حذف کنید. این شما را تنها از مقدار کمی از بار اضافی حفظ می کند، ولی هر قسمت کوچکی شمارش می شوند. استفاده از Session ها در view ها هنگامی که SessionMiddleware فعال شده است، هر شیء HttpRequest – اولین آرگومان برای هر تابع view جنگو – دارای یک attribute، seesion خواهد بود که یک شیء شبیه به دیکشنری می باشد. می تواند درست مثل یک دیکشنری معمولای از آن استفاده کنید. برای مثال در یک view می توانید کاری شبیه به زیر را انجام دهید: # Set a session value: request.session["fav_color"] = "blue" # Get a session value -- this could be called in a different view, # or many requests later (or both): fav_color = request.session["fav_color"] # Clear an item from the session: del request.session["fav_color"] # Check if the session has a given key: if "fav_color" in request.session: ... همچنین می توان از متدهای دیگر دیکشنری همانند keys() و items() در request.session استفاده کنید. تعدادی قوانین ساده برای استفاده ی موثر از session های جنگو وجود دارد: از رشته های معمولی پایتون به صورت کلیدهای دیکشنری در request.session (مخالف integer ها، شیء ها و غیره ...) استفاده کنید. کلیدهای دیکشنری session که با یک خط تیره شروع می شوند، برای استفاده ی داخلی توسط جنگو رزرو شده اند. در عمل، فریم ورک تنها از تعداد کمی از متغیرهای session با خط تیره شروع شده استفاده می کند، ولی در صورتی که در مورد این قبیل متغیرهای اطلاعاتی ندارید (و اگر تمایل دارید با هر تغییراتی در خود جنگو مطابق باشید)، استفاده از پیشوندهای خط تیره، از تداخل جنگو با برنامه ی شما جلوگیری می کند. برای مثال، از کلید session با نام _fav_color، مانند زیر استفاده نکنید: request.session['_fav_color'] = 'blue' # Don't do this! request.session را با یک شیء جدید جایگزین نکنید، و به attribute های آن دسترسی پیدا نکرده و چیزی در آن ها قرار ندهید. از آن مانند یک دیکشنری پایتون استفاده کنید. مثال: request.session = some_other_object # Don't do this! request.session.foo = 'bar' # Don't do this! اجازه دهید مثال های کوچکی را ذکر کنیم. view ساده ی زیر بعد از این که کاربر یک کامنت را پست می کند مقدار True را در یک متغیر has_commented قرار می دهد. جلوگیری از پست کردن بیشتر از یک کامنت توسط کاربر ساده می باشد: def post_comment(request): if request.method != 'POST': raise Http404('Only POSTs are allowed') if 'comment' not in request.POST: raise Http404('Comment not submitted') if request.session.get('has_commented', False): return HttpResponse("You've already commented.") c = comments.Comment(comment=request.POST['comment']) c.save() request.session['has_commented'] = True return HttpResponse('Thanks for your comment!') view ساده ی ورودی یک عضو در سایت: def login(request): if request.method != 'POST': raise Http404('Only POSTs are allowed') try: m = Member.objects.get(username=request.POST['username']) if m.password == request.POST['password']: request.session['member_id'] = m.id return HttpResponseRedirect('/you-are-logged-in/') except Member.DoesNotExist: return HttpResponse("Your username and password didn't match.") و کد زیر خروج یک عضو از سایت که از طریق login() فوق وارد سایت شده است: def logout(request): try: del request.session['member_id'] except KeyError: pass return HttpResponse("You're logged out.") نکته در عمل، روش فوق روشی مناسبی برای ورودی کاربران نمی باشد. فریم ورک authentication که به طور مختصر بحث شده است این وظیفه را برای شما بسیار قدرمتند و با روشی مفید انجام می دهد. این مثال ها عمدا ساده می باشند، به طوری که شما بتوانید به راحتی در جریان کار قرار بگیرید. آزمون کوکی های ارسال شده همانطور که در بالا ذکر شد، نمی توان به هر کوکی ارسال شده ی مرورگر اطمینان کرد. بنابراین، برای راحتی کار، جنگو روشی ساده برای آزمون کوکی های ارسال شده ی مرورگر تهیه کرده است. تنها کافیست request.session.set_test_cookie() را در یک view فراخوانی کرده و request.session.test_cookie_worked() را در view بعدی بررسی کنید – نه در فراخوانی view همسان. این جدایی بین set_test_cookie() و test_cookie_worked به دلیل روش کار کوکی ها ضروری می باشد. زمانی که شما یک کوکی را قرار می دهید، در واقع شما نمی توانید تا درخواست بعدی مرورگر بگویید یک مرورگر آن را ارسال کرده است. استفاده از delete_test_cookie() برای تمیز کردن بعد از خودتان تمرین خوبی می باشد. این عمل را بعد از تایید کارکرد کوکی آزمون انجام دهید. در زیر مثال کاربرد معمولی موضوع فوق وجود دارد: def login(request): # If we submitted the form... if request.method == 'POST': # Check that the test cookie worked (we set it below): if request.session.test_cookie_worked(): # The test cookie worked, so delete it. request.session.delete_test_cookie() # In practice, we'd need some logic to check username/password # here, but since this is an example... return HttpResponse("You're logged in.") # The test cookie failed, so display an error message. If this # were a real site, we'd want to display a friendlier message. else: return HttpResponse("Please enable cookies and try again.") # If we didn't post, send the test cookie along with the login form. request.session.set_test_cookie() return render_to_response('foo/login_form.html') نکته یک بار دیگر، توابع داخلی authentication این بررسی را برای شما انجام می دهند. استفاده از Session ها خارج از view ها به طور داخلی، هر session تنها یک مدل جنگوی معمولی تعریف شده در django.contrib.sessions.models می باشد. هر session توسط هش تصادفی کمتر یا بیشتر 32 حرفی در یک کوکی شناسایی می شود. به این دلیل که session یک مدل معمولی می باشد، می توان با استفاده از API معمولی پایگاه داده ی جنگو به session ها دسترسی پیدا کرد: >>> from django.contrib.sessions.models import Session >>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead') >>> s.expire_date datetime.datetime(2005, 8, 20, 13, 35, 12) برای بدست آوردن داده ی session واقعی، نیاز به فراخوانی get_decoded() می باشد. این موضوع ضروری می باشد، چرا که دیکشنری در قالب بندی رمزی شده ذخیره شده است: >>> s.session_data 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...' >>> s.get_decoded() {'user_id': 42} زمانی که Session ها ذخیره شده اند به طور پیشفرض، جنگو تنها در صورتی که session تغییر کند آن ها را درون پایگاه داده ذخیره می کند – این بدین معنی است که اگر هر کدام از مقادیر دیکشنری آن اختصاص داده شود یا حذف شود: # Session is modified. request.session['foo'] = 'bar' # Session is modified. del request.session['foo'] # Session is modified. request.session['foo'] = {} # Gotcha: Session is NOT modified, because this alters # request.session['foo'] instead of request.session. request.session['foo']['bar'] = 'baz' برای تغییر این رفتار پیشفرض، مقدار SESSION_SAVE_EVERY_REQUEST را True قرار دهید. در صورتی که SESSION_SAVE_EVERY_REQUEST، True باشد، جنگو در هر درخواست تنها درون پایگاه داده ذخیره می کند، حتی اگر تغییر نکرده باشد. توجه داشته باشید که کوکی session تنها زمانی که یک session ساخته شده یا تغییر کرده باشد فرستاده می شود. در صورتی که SESSION_SAVE_EVERY_REQUEST، True باشد، کوکی session در هر درخواست فرستاده خواهد شد. به طور یکسان، بخش expires از یک کوکی session در هر بار که کوکی session فرستاده شود به روز رسانی می شود. Session های Browser-Length در مقابل Session های مقاوم ممکن است متوجه شده باشید که کوکی گوگل فرستاده شده به ما در ابتدای این آموزش حاوی expires=Sun، 17-Jan-2038 19:14:07 GMT; بود. کوکی ها به طور اختیاری می توانند حاوی یک تاریخ انقضا باشند که مرورگر را در هنگام حذف کوکی آگاه می سازد. در صورتی که یک کوکی حاوی مقدار انقضا نباشد، زمانی که کاربر پنجره ی مرورگر را ببندد از بین می رود. می توان رفتار فریم ورک یا چارچوب session را رابطه با تنظیم SESSION_EXPIRE_AT_BROWSER_CLOSE کنترل کرد. به طور پیشفرض، مقدار SESSION_EXPIRE_AT_BROWSER_CLOSE، False در نظر گرفته شده است، که بدین معنی می باشد که کوکی های session در مرورگرهای کاربران برای SESSION_COOKIE_AGE ثانیه (که پیشفرض آن دو هفته یا 1,209,600 ثانیه) می باشد. در صورتی که نمی خواهید مردم در هر بار که مرورگر را باز می گنند به سایت log in نشوند از این تنظیم می توانید استفاده کنید. در صورتی که مقدار SESSION_EXPIRE_AT_BROWSER_CLOSE، True باشد، جنگو از کوکی های browser-length استفاده خواهد کرد. تنظیمات دیگر Session در کنار تنظیمات ذکر شده، چند تنظیم دیگر بر نحوه ی استفاده ی فریم ورک session جنگو از کوکی ها تاثیر می گذارد، همانطور که در جدول زیر نشان داده شده است. تنظیم توضیح پیشفرض SESSION_COOKIE_DOMAIN دامنه ی مورد استفاده برای کوکی های session. برای این تنظیم یک رشته مانند ".example.com" برای کوکی های cross‑domain قرار دهید، یا برای یک کوکی استاندارد از None استفاده کنید. None SESSION_COOKIE_NAME نام کوکی ای که برای session ها استفاده می شود. این می تواند هر رشته ای باشد. "sessionid" SESSION_COOKIE_SECURE در دست ترجمه/تالیف .... در صورتی که این تنظیم True باید، کوکی به صورت "امن" علامت گذاری خواهد شد، که بدین معناست که مرورگر ها مطمئن خواهند بود که کوکی تنها از طریق HTTPS ارسال شده است. False جزئیات فنی محض کنجکاوی، در زیر چند نکته ی فنی درباره ی کار عملکرد داخلی فریم ورک session بیان شده است: در دست ترجمه/تالیف .... برای کسب اطلاعات درمورد کارکرد ماژول داخلی پایتون یعنی pickle مستندات پایتون مراجعه کنید. داده ی session در جدول پایگاه داده با نام django_session ذخیره می شود. در دست ترجمه/تالیف .... در صورتی که هرگز به request.session دسترسی ندارید، جنگو به آن جدول پایگاه داده مراجعه می کند. جنگو در صورت نیاز یک کوکی ارسال می کند. در صورتی که هیچ داده ی session ای قرار نگرفته باشد، جنگو کوکی session ای ارسال نخواهد کرد (مگر اینکه مقدار SESSION_SAVE_EVERY_REQUEST، True قرار داده شده باشد). فریم ورک session جنگو به طور کلی و تنها بر اساس کوکی می باشد. در دست ترجمه/تالیف .... این یک تصمیم طراحی بین المللی می باشد. قرار دادن session ها در URL ها نه تنها باعث زشت شدن URL ها می شود، بلکه همچنین سایت شما را برای یک فرم خاص از سرقت هدر Referer آسیب پذیر می سازد. در صورتی که هنوز کنجکاو هستید، منبع بسیار آسان می باشد؛ برای اطلاعات بیشتر به django.contrib.sessions نگاهی بیاندازید. کاربران و تصدیق session ها راه تداوم داده را از بین چندین درخواست مرورگر به ما می دهند؛ دومین قسمت معادله استفاده از آن session ها برای ورود کاربر می باشد. البته، نمی توانیم به راحتی به هر کاربری که وارد می شود اعتماد کنیم، بنابراین نیاز به تصدیق کردن آن ها در طول مسیر می باشد. به طور طبیعی، جنگو ابزاری را جهت این وظایف مشترک (و بسیاری دیگر) تهیه کرده است. سیستم تصدیق کاربر جنگو حساب ها کاربر، حق دسترسی ها، و session های بر پایه ی کوکی کاربر را کنترل می کند. این سیستم اغلب به صورت یک سیستم auth/auth (تصدیق و تصدیق) مورد مراجعه قرار گرفته است. آن نام عدم اعتبار کاربران را اغلب با یک پردازش دو مرحله ای تشخیص می دهد. نیاز به نکات زیر می باشد: تصدیق کاربری که ادعای کاربر بودن می کند (معمولا توسط بررسی یک نام کاربری و رمز عبور در مقابل یک پایگاه داده از کاربران) تصدیق این که کاربر مجاز به اجرای برخی اعمال داده شده (معمولا توسط بررسی در یک جدول از حق دسترسی ها) می باشد. در ادامه ی این نیازمندی ها، سیستم auth/auth جنگو حاوی تعدادی از بخش ها می باشد: کاربران: افرادی که در سایت شما عضویت دارند حق دسترسی ها: پرچم های دودویی (yes/no) طراحی شده برای مشخص کردن اینکه آیا یک کاربر می تواند یک وظیفه ی خاص را انجام دهد گروه ها: روشی عمومی جهت بکار بردن لیبل ها و حق دسترسی به بیشتر از یک کاربر پیام ها: روشی ساده برای به صف کردن و نمایش سیستم پیام ها به کاربران در صورتی که از ابزار مدیر استفاده کرده اید، شما بسیاری از این ابزار را مشاهده کرده اید، و در صورتی که کاربران و گروه ها را در ابزار مدیر ویرایش کرده باشید، شما در واقع در جدول پایگاه داده ی سیستم auth داده ها را ویرایش کرده اید. فعال ساختن پشتیبانی تصدیق همانند ابزار session، پشتیبانی تصدیق به صورت یک برنامه ی جنگو در django.contrib که نیاز به نصب شدن دارد همراه است. همچنین مانند ابزار session، باید به طور پیشفرض نصب شده باشد، ولی در صورتی که شما آن را حذف کرده باشید، نیاز است مراحل زیر را برای نصب آن دنبال کنید: اطمینان حاصل کنید فریم ورک session همانطور که قبلا در این آموزش از کتاب توضیح داده شد نصب شده باشد. پیگیری کاربران بدیهی است که مستلزم کوکی ها می باشد، و در نتیجه در فریم ورک session ساخته می شود. 'django.contrib.auth' را در تنظیم INSTALLED_APPS قرار داده و دستور manage.py syncdb را جهت نصب جداول پایگاه داده ی مناسب اجرا کنید. از وجود 'django.contrib.auth.middleware.AuthenticationMiddleware'درون تنظیم MIDDLEWARE_CLASSES اطمینان حاصل کنید – بعد از SessionMiddleware. با انجام مراحل فوق، همه چیز برای سر و کار داشتن با کاربران در توابع view آماده می باشد. رابط اصلی که شما برای دسترسی به کاربران در یک view از آن استفاده خواهید کرد request.user می باشد؛ این یک شیء است که کاربر فعلی وارد شده به سایت را نشان می دهد. در صورتی که کاربر وارد نشده باشد، به جای آن یک شیء AnonymousUser خواهد بود (برای جزئیات بیشتر به ادامه ی این بخش نگاه بیاندازید). می توانید به سادگی در صورتی که یک کاربر وارد شده است، با متد is_authenticated() تشخیص دهید: if request.user.is_authenticated(): # Do something for authenticated users. else: # Do something for anonymous users. استفاده از کاربران هنگامی که شما یک کاربر دارید – اغلب از request.user، ولی از طریق یکی از روش های مختصر توضیح داده شده – تعدادی از فیلدها و متدهای در دسترس در آن شیء دارید. شیء های AnonymousUser برخی از این رابط ها را تقلید کرده است، ولی نه تمام آن را، بنابراین باید همواره user.is_authenticated() را قبل از آنکه کاربری را که با آن سر و کار دارید را با حسن نیت تصور کنید بررسی کنید.. جدول 3-14 و 4-14 فیلد ها و متد ها را به ترتیب در شیء های User لیست کرده است. فیلد توضیح username الزامی؛ 30 حرف یا کمتر. تنها حروف الفبایی (حروف الفبا، عددها، و خط تیره) First_name اختیاری؛ 30 حرف یا کمتر Last_name اختیاری؛ 30 حرف یا کمتر email اختیاری؛ آدرس پست الکترونیک password الزامی؛ یک هش و ابر داده از رمز عبور (جنگو رمز عبور خام را ذخیره نمی کند). برای اطلاعات بیشتر به بخش "رمزهای عبور" مراجعه کنید. Is_staff Boolean. اینکه کاربر می تواند به سایت مدیر دسترسی پیدا کند یا خیر را مشخص می کند. Is_active Boolean. این که این حساب می تواند برای ورود استفاده شده باشد یا خیر را مشخص می کند. این پرچم را به جای حذف حساب ها مقدار False قرار دهید. Is_superuser Boolean. اینکه این کاربر دارای تمام دسترسی ها بدون اختصاص دادن آن ها به طور واضح می باشد یا خیر را مشخص می کند. Last_login یک datetime از آخرین ورود کاربر. به طور پیشفرض زمان/تاریخ فعلی در آن قرار دارد. date_joined تشخیص datetime زمانی که حساب ساخته شده است. به طور پیشفرض زمانی که حساب ساخته شده است مقدار آن زمان/تاریخ فعلی می باشد. متد توضیح is_authenticated() همواره شیء های User "واقعی" مقدار True بر می گرداند. در صورتی که کاربر تصدیق شده باشد این روشی برای گفتن می باشد. این هیچ اشاره ای به حق دسترسی ها نداشته و فعال بودن کاربر را نیز بررسی نمی کند. تنها این را نشان می دهد که کاربر با موفقیت تصدیق شده است. is_anonymous() تنها برای شیء های AnonylousUser مقدار True بر می گرداند (و مقدار False برای شیء های User "واقعی"). عموما، باید استفاده از is_authenticated() را برای این متد ترجیح دهید. get_full_name() first_name را بعلاوه ی last_name، با یک فاصله در بین آن ها بر می گرداند. set_password(passwd) رمز عبور کاربر را برای رشته ی خام داده شده قرار می دهد، در دست ترجمه/تالیف .... در واقع این متد شیء User را ذخیره نمی کند. check_password(passwd) در صورتی که رشته ی خام داده شده رمز عبور درست کاربر باشد مقدار True بر می گرداند. در دست ترجمه/تالیف .... get_group_permissions() لیستی از رشته های حق دسترسی که کاربر از طریق گروهی که به آن تعلق دارد در اختیار دارد را بر می گرداند. get_all_permissions() لیستی از رشته ی حق دسترسی که کاربر دارد، هم حق دسترسی گروه هم کاربر. has_perm(perm) در صورتی که کاربر دارای حق دسترسی مشخص شده باشد مقدار True بر می گرداند و perm قالب بندی "package.codename" می باشد. در صورتی که کاربر غیر فعال باشد، این متد همواره مقدار False بر می گرداند. has_perms(perm_list) در صورتی که کاربر دارای تمام حق دسترسی های تعیین شده باشد مقدار True بر می گرداند. در صورتی که کاربر غیر فعال باشد، هموراه این متد مقدار False بر می گرداند. has_module_perms(app_lable) در صورتی که کاربر هیچ حق دسترسی ای در app_lable داده شده نداشته باشد مقدار True بر می گرداند. در صورتی که کاربر غیر فعال باشد، این متد هموراه مقدار False بر می گرداند. get_and_delete_messages() لیستی از شیء های Message در صف کاربر بر می گرداند و پیام های صف را حذف می کند. email_user(subj, msg) یک پست الکترونیکی به کاربر می فرستد. این پست الکترونیکی از تنظیم DEFAULT_FROM_EMAIL فرستاده می شود. همچنین می توانید یک آرگومان سوم به نام from_email جهت override آدرس From در پست الکترونیکی ارسال کنید. در پایان، شیء های User دارای دو فیلد many-to-many می باشند: groups و permissions. شیء های User می توانند همانند فیلدهای many-to-many دیگر به شیء های مربوط به خود دسترسی پیدا کنند: # Set a user's groups: myuser.groups = group_list # Add a user to some groups: myuser.groups.add(group1, group2,...) # Remove a user from some groups: myuser.groups.remove(group1, group2,...) # Remove a user from all groups: myuser.groups.clear() # Permissions work the same way myuser.permissions = permission_list myuser.permissions.add(permission1, permission2, ...) myuser.permissions.remove(permission1, permission2, ...) myuser.permissions.clear()
  5. مقاله معیارهای کیفی سنجش فضا نوشته جهانشاه پاکزاد که در شماره سی و نهم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. معیارهای کیفی سنجش فضا.pdf
  6. مقاله مدل های اداره امور شهر؛ تجربه آلمان فدرال ترجمه و تدوین حسین ایمانی جاجرمی که در شماره سی و نهم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. مدل های اداره امور شهر تجربه آلمان فدرال.pdf
  7. مقاله خیابان ها می توانند باعث مرگ شهرها شوند : هشدار به جهان سوم (رهنمودهایی برای طراحی خیابان در شهرهای جهان سوم) نوشته دنالد اپلیارد و ترجمه نوین تولایی که در شماره سی و نهم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. خیابان ها می توانند باعث مرگ شهرها شوند.pdf
  8. بخش سوم از معرفی پروژه های معماری معاصر کشورهای اسلامی ترجمه و تدوین حمید حسینمردی که در شماره سی و نهم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. معرفی پروژه های معاصر کشورهای اسلامی.pdf
  9. با پسورد www.noandishaan.com می تونید اکسترکت کنید
  10. Mohammad Aref

    آشنایی با کاربران انجمن

    چون نبودی خوش اومدی
  11. Mohammad Aref

    ایجاد صفحه اطلاع رسانی کرونا

    فکر کردم یه تاپیکه که قراره اطلاع رسانیا توش گذاشته بشه پیشنهاد بود همون قسمت مدیریت میگفتی بهتر بود که. من خودم خیلی با تلگرام اینا کار نمیکنم. کانال اصلی تلگرام سایتو نمیرسم چه برسه به این که یه کانال موقت و بات بسازم واسه این کار
  12. جزوه بیان معماری ویژه دانشجویان شهرسازی که در 33 صفحه تهیه شده رو به صورت PDF از لینک زیر می تونید دانلود کنید. دانلود جزوه
  13. Mohammad Aref

    آموزش جنگو Django Framework

    تولید محتوای غیر html ای در جنگو معمولا هنگامی که درباره ی deploy کردن وب سایت ها صحبت می شود، موضوع صحبت درباره ی تولید HTML می باشد. البته که موارد بسیار دیگری نیز وجود دارد؛ ما از وب برای توزیع داده در تمام قابل بندی ها استفاده می کنیم: RSS، PDF، عکس ها و غیره .... تاکنون، تمرکز بر روی تولید HTML بوده است، ولی در این آموزش، از این مسیر منحرف شده و به استفاده ی جنگو برای تولید انواع دیگر محتویات به غیر از HTML خواهیم پرداخت. جنگو دارای ابزار داخلی مناسبی می باشد که می توان برای تولید برخی محتوای به غیر از HTML از آن استفاده کرد: Feed های پیوند RSS/Atom Sitemap ها (یک قالب بندی XML توسعه داده شده توسط گوگل که به موتورهای جستجو راهنمایی هایی را می دهد) هر کدام از ابزار فوق در این قسمت بررسی خواهند شد، ولی ابتدا قواعد اولیه را پوشش خواهیم داد. اصول اولیه: view ها و MIME-type ها آموزش view و urlconf جنگو را بخاطر بیاورید که یک تابع view به سادگی یک تابپ پایتون می باشد که یک درخواست وب را دریافت کرده و یک پاسخ وب را بر می گرداند. این پاسخ می تواند محتویات HTML از صفحه ی وب یا یک تغییر مسیر، یا یک خطای 404، یا یک سند XML، یا حتی یک تصویر ... و یا هر چیز دیگری باشد. به طور رسمی تر، یک تابع view جنگو باید قبول یک نمونه ی HttpRequest به صورت اولین آرگومان برگرداندن یک نمونه ی HttpResponse نکته ی کلیدی جهت برگرداندن محتوای غیر HTML ای از یک view درون کلاس HttpResponse قرار دارد، به ویژه آرگومان mimetype. با قرار دادن mimetype ، می توان به مرورگر نشان داد که یک پاسخ از قالب بندی متفاوت را بر مر گردانیم. برای مثال، اجازه دهید به view زیر که یک تصویر با قالب بندی PNG را بر می گرداند را بررسی کنیم، تنها فایل را از حافظه خوانده ایم: from django.http import HttpResponse def my_image(request): image_data = open("/path/to/my/image.png", "rb").read() return HttpResponse(image_data, mimetype="image/png") همین! در صورتی که شما مسیر تصویر را در فراخوانی open() با یک مسیر برای یک تصویر واقعی جا به جا کنید، می توانید از این view به سادگی برای نمایش یک تصویر استفاده کنید، و مرورگر به درستی آن را نمایش خواهد داد. نکته ی مهم دیگر که باید در نظر داشت این است که، شیء های HttpResponse با API استاندارد پایتون یعنی "file like object" کار می کنند. این بدین معناست که می توان، از یک نمونه ی HttpResponse را در هر جای پایتون (یا یک کتابخانه ی third-party) که انتظار یک فایل را دارد استفاده کنید. برای مثالی از نحوه ی کارکرد آن، اجازه دهید نگاهی به تولید CSV با جنگو بیاندازیم. تولید CSV CSV یک قالب بندی ساده می باشد که معمولا توسط نرم افزار spreadsheet استفاده می شود. اساسا یک سری از ردیف های جدول با هر سلول در ردیف جدا شده توسط یک علامت کاما می باشد (CVS مخفف comma‑seperated values می باشد). برای مثال، در زیر برخی داده های "unruly" مسافران هوایی در قالب بندی CSV می باشد. Year,Unruly Airline Passengers 1995,146 1996,184 1997,235 1998,200 1999,226 2000,251 2001,299 2002,273 2003,281 2004,304 2005,203 2006,134 2007,147 نکته لیست قبلی حاوی اعداد واقعی می باشد! این لیست از مدیریت هواپیمایی فدرال آمریکا آمده است. هر چند CSV ساده به نظر می رسد، قالب بندی جزئیات آن دارای توافق جهانی نمی باشد. قسمت های مختلف نرم افزار CVS های مختلفی را تولید و استفاده می کند. خوشبختانه، پایتون دارای یک کتابخانه ی استاندار CSV با نام cvs می باشد. به دلیل آنکه ماژول csv به صورت file-like objects کار می کند، استفاده از آن ساده تر از HttpResponse می باشد: import csv from django.http import HttpResponse # Number of unruly passengers each year 1995 - 2005. In a real application # this would likely come from a database or some other back-end data store. UNRULY_PASSENGERS = [146,184,235,200,226,251,299,273,281,304,203] def unruly_passengers_csv(request): # Create the HttpResponse object with the appropriate CSV header. response = HttpResponse(mimetype='text/csv') response['Content-Disposition'] = 'attachment; filename=unruly.csv' # Create the CSV writer using the HttpResponse as the "file." writer = csv.writer(response) writer.writerow(['Year', 'Unruly Airline Passengers']) for (year, num) in zip(range(1995, 2006), UNRULY_PASSENGERS): writer.writerow([year, num]) return response کد فوق باید بسیار واضح باشد، ولی یک نکاتی ویژه برای ذکر کردن وجود دارد: Response به جای mimetype پیشفرض یعنی text/html با mimetype مورد نظر یعنی text/csv معین شده است. متغیر response یک Content-Disposition header اضافه دریافت می کند که حاوی نام فایل CSV می باشد. این header (بخش "attachment") مرورگر را برای یک جا جهت ذخیره کردن فایل به جای نمایش آن بر می انگیزد. نام این فایل دلخواه می باشد؛ آن را هر چه که می خواهید نام گذاری کنید. این توسط مرورگر در دیالوگ "Save As" استفاده خواهد شد. جهت اختصاص دادن یک header در یک HttpResponse ، تنها کافیست به صورت یک دیکشنری و مجموعه ای از کلید و ارزش ها با آن رفتار کنید. در دست ترجمه/تالیف ... به داخل API، CSV_generation ساده می باشد: تنها response را به صورت اولین آرگومان به csv.writer ارسال کنید. برای هر ردیف در فایل CSV، فراخوانی writer.writerow مانند شیءی به صورت یک لیست یا تاپل آن را ارسال می کند. ماژول CSV برای گذاشتن کتیشن برای ما محتاط می باشد، بنابراین نگرانی ای درباره ی رد کردن رشته های با کتیشن یا کاما در آن ها نخواهید داشت. تنها اطلاعات را برای writerow() ارسال می کند. این الگوی کلی ای می باشد که شما در هر زمان که نیاز به برگرداندن محتوای غیر HTML ای دارید از آن استفاده خواهید کرد: ساختن یک شیء HttpResponse (با یک mimetype ویژه)، ارسال کردن آن به چیزی که انتظار یک فایل را دارد، و سپس برگرداند یک response. اجازه دهید به نگاهی تعداد بیشتری از مثال بیاندازیم. ساختن PDF Portable Document Format (PDF) یک قالب بندی توسعه یافته توسط Adobe می باشد که برای نمایش اسناد قابل چاپ استفاده می شود، قالب بندی pixel-perfect کامل، فونت های جاسازی شده، و گرافیک دو بعدی. می توان یک PDF را معادل دیجیتال یک سند چاپ شده دانست؛ در واقع، PDF ها اغلب در توزیع اسناد به قصد چاپ آن ها استفاده می شود. می توان به سادگی PDF ها را با پایتون و جنگو، با تشکر از کتابخانه ی عالی منبع باز ReportLab تولید کرد (htt://www.reportlab.org/rl_toolkit.html). مزیت تولید فایل های PDF به طور پویا این است که، می توانید PDF های سفارشی را برای اهداف مختلف ایجاد کنید. نصب ReportLab قبل از تولید PDF نیاز می باشد ReportLab را نصب کنید که معمولا خیلی ساده می باشد: تنها کافیست کتابخانه ی آن را از [Hidden Content] دانلود و نصب کنید. نکته در صورتیکه توزیع مدرن لینوک استفاده می کنید، ممکن است package management خود را قبل از نصب ReportLab بررسی کنید. اغلب repository های پکیج دارای ReportLab اضافه شده می باشند. برای مثال، در صورتی که از ubuntu استفاده می کنید، به سادگی دستور apt-get install python-reportlab این کار را انجام خواهد داد. راهنمای کاربر (به طور طبیعی تنها به صورت یک فایل PDF در دسترس می باشد) در [Hidden Content] دارای دستور العمل اضافه می باشد. می توان نصب بودن ReportLab را با import کردن آن در interactive interpreter پایتون آزمایش کرد: >>> import reportlab در صورتیکه دستور فوق هیچ خطایی را ایجاد نکند، نصب درست انجام شده است. نوشتن view همانند CSV، تولید PDF ها به صورت پویا با جنگو ساده می باشد، چرا که API مخصوص ReportLab به صورت file‑like object عمل می کند. در زیر مثال "Hello World" را مشاهده می کنید: from reportlab.pdfgen import canvas from django.http import HttpResponse def hello_pdf(request): # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment; filename=hello.pdf' # Create the PDF object, using the response object as its "file." p = canvas.Canvas(response) # Draw things on the PDF. Here's where the PDF generation happens. # See the ReportLab documentation for the full list of functionality. p.drawString(100, 100, "Hello world.") # Close the PDF object cleanly, and we're done. p.showPage() p.save() return response نکاتی در مورد کد فوق: در کد فوق از mimetype مخصوص PDF یعنی application/pdf استفاده شده است. این حالت به مرورگرها می گوید که سند به جای یک فایل HTML، یک فایل PDF می باشد. در صورتی که این اطلاعات را جا بیاندازید، مرورگرها پاسخ را به صورت HTML تفسیر می کنند، نتیجه ی به هم ریخته و نا مفهومی در پنجره ی مرورگر ایجاد خواهد کرد. در دست ترجمه/تالیف ...: تنها کافیست response را به صورت آرگومان اول برای canvas.Canvas ارسال کنید. کلاس Canvas انتظار یک file-like object را دارد، و شیء های HttpResponse نیز همینطور. تمام متدهای در دست ترجمه/تالیف ... در پایان، فراخوانی showPage() و save() در فایل PDF با اهمیت می باشد – در غیر این صورت در فایل PDF خراب خواهد شد. PDF های پیچیده در صورتی که یک سند پیچیده ی PDF (یا هر قطعه داده ی بزرگ) را ایجاد می کنید، استفاده کردن از کتابخانه ی cStringIO به صورت یک محل نگهداری موقت برای فایل PDF خودتان را بررسی کنید. کتابخانه ی cStringIO یک رابط file-like object را تهیه می کند که برای حداکثر بهره وری در C نوشته شده است. در زیر مثال "Hello World" قبلی با استفاده از cStringIO باز نویسی شده است: from cStringIO import StringIO from reportlab.pdfgen import canvas from django.http import HttpResponse def hello_pdf(request): # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment; filename=hello.pdf' temp = StringIO() # Create the PDF object, using the StringIO object as its "file." p = canvas.Canvas(temp) # Draw things on the PDF. Here's where the PDF generation happens. # See the ReportLab documentation for the full list of functionality. p.drawString(100, 100, "Hello world.") # Close the PDF object cleanly. p.showPage() p.save() # Get the value of the StringIO buffer and write it to the response. response.write(temp.getvalue()) return response امکانات دیگر مجموعه ی کاملی از انواع دیگر محتویاتی که می توان در پایتون تولید کرد وجود دارد: فایل های ZIP: کتابخانه ی استاندارد پایتون حاوی ماژول zipfile می باشد، که می تواند فایل های ZIP را هم بنویسد و هم بخواهند. می توان برای تولید بایگانی کردن تعدادی از فایل های مورد نیاز یا فشرده ساختن اسناد بزرگ از آن استفاده کرد. همچنین می توان فایل های TAR را با استفاده از ماژول tarfile در کتابخانه ی استاندارد پایتون تولید نمود. تصاویر پویا: کتابخانه ی تصویر پایتون (PIL؛ [Hidden Content]) یک جعبه ابزار خارق العاده برای تولید تصاویر (PNG، JPEG، GIF و مقدار بیشتری) می باشد. شما می توانید برای کاهش اندازه ی تصاویر به عکس های ریز به طور خودکار، تصاویر چندگانه ی مرکب در یک فریم و یا حتی برای انجام پردازش تصویر تحت وب از آن استفاده کنید. طرح ها و نمودارها: تعدادی کتابخانه ی قدرتمند برای طرح ها و نمودارها در پایتون وجود دارد که می توان برای تولید نقشه های مورد نیاز، نمودارها، طرح ها و گرافیک ها از آن استفاده می کرد. مسلما نمی توان تمام آن ها را در اینجا لیست کرد، بنابراین تعداد از موارد برجسته در زیر نام برده شده اند: matplotlib ([Hidden Content]) می تواند برای تولید نوعی از طرح های با کیفیت بالا که معمولا توسط MatLab یا Matematica تولید شده اند استفاده شود. pygraphviz ([Hidden Content])، یک رابط برای لایه ی ابزار گرافیک Graphviz ([Hidden Content]) می باشد، که می تواند برای تولید نمودارهای ساخت یافته از گرافیک ها و شبکه ها استفاده شود. به طور کلی، هر کتابخانه ی پایتون قابل ایجاد درون یک فایل می تواند درون جنگو استفاده شود. امکانات عظیم می باشند. اکنون که با قواعد اولیهه ی تولید محتوای غیر HTML ای، آشنا شدیم، اجازه دهید تصورمان را یک درجه افزایش دهیم. جنگو تعداد ابزار بسیار جذاب برای تولید انواع رایج محتوای غیر HTML ای ارائه می دهد. فریم ورک یا چارچوب Syndication Feed جنگو یک فریم ورک یا چارچوب سطح بالا syndication-feed-generating را ارائه می کند که ایجاد RSS و Atom را ساده می کند. RSS چیست؟ Atom جیست؟ RSS و Atom هر دو XML-based می باشند، که شما می توانید برای تهیه ی به روز رسانی به طور خودکار "feed" های محتویات سایت از آن استفاده کنید. برای اطلاعات بیشتر در مورد RSS می توانید به [Hidden Content] مراجعه کنید، و در [Hidden Content] نیر می توانید اطلاعاتی راجع به Atom بدست آوردید. برای ایجاد هر syndication feed، همه باید یک کلاس کوچک پایتون بنویسند. می توانید هر مقدار "feed" که می خواهید بسازید. فریم ورک یا چارچوب سطح بالای feed-generating یک view می باشد که به طور قرارداد به /feeds/ وصل شده است. جنگو از باقی مانده ی URL (هر چیزی بعد از /feeds/) برای تعیین feed برای برگرداندن استفاده می کند. برای ایجاد یک feed، شما یک Feed Class خواهید نوشت و در URLconf خود به آن اشاره می کنید. مقدار دهی اولیه برای فعال کردن syndication feed ها روی سایت جنگوی خود، URLconf زیر را اضافه کنید: (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds} ), این خط به جنگو می گوید؛ از فریم ورک RSS جهت کنترل تمام URL هایی که با "feeds/" شروع می شوند استفاده کند. (می توانید پیشوند "feeds/" را برای متناسب ساختن با نیازهای خودتان تغییر دهید.) این خط URLconf دارای یک آرگومان اضافی می باشد: {'feed_dict': feeds}. از این آرگومان اضافه جهت ارسال feed هایی که باید تحت URL منتشر شده باشند استفاده کنید. به طور خاص، feed_dict باید یک دیکشنری باشد که نام URL را به کلاس Feed مرتبط می کند. می توانید feed_dict را درون خود URLconf تعریف کنید. در زیر مثال کامل URLconf را ملاحظه می کنید: from django.conf.urls.defaults import * from mysite.feeds import LatestEntries, LatestEntriesByCategory feeds = { 'latest': LatestEntries, 'categories': LatestEntriesByCategory, } urlpatterns = patterns('', # ... (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}), # ... ) مثال قبلی دو feed را در نظر گرفته است: feed اول که با LatestEntries نشان داده شده است و در feeds/latest/ قرار خواهد گرفت. دومین feed که با latestEntriesByCategory نمایش داده شده و در feeds/categories قرار خواهد گرفت. زمان راه اندازی، نیاز خواهید داشت خود کلاس های Feed را تعریف کنید. یک کلاس Feed یک کلاس ساده ی پایتون می باشد که یک syndication feed را نشان می دهد. یک feed می تواند ساده باشد (مانند یک feed "سایت اخبار"، یا یک feed اولیه که آخرین ورودی های یک بلاگ را نمایش می دهد) یا بسیار پیچیده (مانند یک feed که تمام ورودی های بلاگ در یک طبقه بندی خاص جایی که طبقه بندی متغیر می باشد نمایش می دهد). کلاس های Feed باید از کلاس django.contrib.syndication.feeds.Feed مشتق شوند. آن ها می توانند در هر جایی از درخت کد شما قرار بگیرند. یک Feed ساده مثال ساده ی زیر یک Feed از پنج ورودی آخر برای بلاگ داده شده را توضیح می دهد: from django.contrib.syndication.feeds import Feed from mysite.blog.models import Entry class LatestEntries(Feed): title = "My Blog" link = "/archive/" description = "The latest news about stuff." def items(self): return Entry.objects.order_by('-pub_date')[:5] نکات قابل توجه در کد فوق از این قرار می باشند: کلاس فوق از کلاس django.contrib.syndication.feeds.Feed مشتق شده است. title، link و description به ترتیب با المان های <title>، <link> و <description> برابر می باشند. items() یک متد می باشد که به سادگی یک لیست از شیء هایی که باید به صورت المان های <item> شامل شده در feed باشند بر می گرداند. اگر چه مثال فوق شیء های Entry را که از API پایگاه داده ی جنگو استفاده می کنند بر می گرداند، item() نباید نمونه های مدل را بر گرداند. تنها یک گام بیشتر وجود دارد. در یک RSS feed، هر <item> دارای یک <title>، <link> و <description> می باشد. نیاز است داده ای که قرار است برای درون آن المنت ها قرار گیرد، به فریم ورک گفته شود. برای تعیین محتویات <title> و <description>، template های جنگو را با نام های feeds/latest_title.html و feeds/latest_description.html ایجاد کنید، جایی که latest، slug تعیین شده در URLconf برای feed داده شده می باشد. توجه داشته باشید که پسوند .html الزامی می باشد. سیستم RSS آن template را برای هر آیتم render می کند، ارسال آن دو متغیر template: obj: شیء فعلی (هر کدام از شیء هایی که در items() بر گردانده شده است). site: نمایش یک شیء django.models.core.sites.Site سایت فعلی. این برای {{ site.domain }} یا {{ site.name }} مفید می باشد. در صورتیکه یک template برای title یا description ایجاد نکنید، فریم ورک به صورت پیشفرض از template، {{ obj }} استفاده می کند – این template نمایش رشته ی معمولی شیء می باشد. (برای شیء های مدل، این متد __unicode__() خواهد بود.) همچنین می توان نام این دو template را از طریق تعیین title_template و description_template به صورت attribute های کلاس Feed تغییر داد. برای تعیین محتویات <link>، دو option وجود دارد. برای هر آیتم در items()، جنگو ابتدا سعی می کند یک متد get_absolute_url() روی آن شیء اجرا کند. در صورتی که متد وجود نداشته باشد، سعی می کند یک متد item_link() در کلاس Feed فراخوانی کند، ارسال آن یک پارامتر تنها، item، که خود شیء می باشد. هر دوی get_absolute_url() و item_link() باید URL ایتم را به صورت یک رشته ی معمولی پایتون بر گردانند. برای مثال LatestEntries قبلی، می توانیم template های بسیار ساده ی feed را داشته باشیم. latest_title.html حاوی: {{ obj.title }} و latest_description.html حاوی: {{ obj.description }} این اغلب بسیار ساده می باشد ... یک Feed پیچیده تر فریم ورک همچنین feed های پیچیده تر را نیز از طریق پارامترها پشتیبانی می کند. برای مثال، تصور کنید بلاگ شما یک RSS feed برای هر تگ مجزایی که شما برای طبقه بندی ورودی های خودتان استفاده کرده اید ارائه می کند. ساختن یک کلاس Feed جدا برای هر تگ احمقانه می باشد؛ نقض قانون "Don't Repeat Yourself" (DRY) می باشد. در عوض، چارچوب syndication به شما اجازه می دهد feed های جنریکی ایجاد کنید که آیتم های متکی بر اطلاعات در آدرس feed ها را گردانند. feed های تعیین تگ شما می تواند مانند زیر URL ها را استفاده کنند: [Hidden Content]: ورودی های فعلی علامت زده شده با "python" را بر می گرداند [Hidden Content]: ورودی های فعلی علامت زده شده با "cats" را بر می گرداند slug در اینجا "tags" می باشد. فریم ورک syndication، bit های URL اضافه ی بعد از slug را می بیند – 'python' و 'cats' – و به شما جهت گفتن معنی آن bit های URL و نحوه ی تاثیر آن ها در دست ترجمه/تالیف ... یک مثال این موضوع را واضح تر می کند. در کد زیر feed های تگ تعیین را ملاحظه می کنید: from django.core.exceptions import ObjectDoesNotExist from mysite.blog.models import Entry, Tag class TagFeed(Feed): def get_object(self, bits): # In case of "/feeds/tags/cats/dogs/mice/", or other such # clutter, check that bits has only one member. if len(bits) != 1: raise ObjectDoesNotExist return Tag.objects.get(tag=bits[0]) def title(self, obj): return "My Blog: Entries tagged with %s" % obj.tag def link(self, obj): return obj.get_absolute_url() def description(self, obj): return "Entries tagged with %s" % obj.tag def items(self, obj): entries = Entry.objects.filter(tags__id__exact=obj.id) return entries.order_by('-pub_date')[:30] در کد فوق اصل اولیه ی الگوریتم فریم ورک RSS وجود دارد، به توجه به این کلاس و یک درخواست برای URL مورد نظر یعنی /feeds/tags/python/: فریم ورک آدرس /feeds/tags/python/ را دریافت می کند و ملاحظه می کند که یک تکه ی اضافی از URL بعد از slug وجود دارد. فریم ورک رشته ی باقی مانده را از توسط حرف ("/") جدا کرده و متد کلاس Feed یعنی get_object را فراخوانی کرده و bit ها را به آن ارسال می کند. در این مورد، bits مورد نظر ['python'] می باشد. برای یک درخواست به /feeds/tags/python/django/، bit ها ['python', 'django'] می باشند. get_object() مسئول بازیابی شیء Tag داده شده از bit های داده شده می باشد. در این مورد، get_object() برای بازیابی Tag از API پایگاده داده ی جنگو استفاده می کند. توجه داشته باشید که get_object() در صورتیکه پارامتر های غیر معتبر داده شود، باید خطای django.core.exceptions.ObjectDoesNotExist ایجاد کند. هیچ try/except ای در اطراف فراخوانی Tag.DoesNotExist وجود ندارد، زیرا این کار ضروری نمی باشد. آن تابع زمان شکست Tag.DoesNotExist ایجاد می کند، و Tag.DoesNotexist از OjbectDoesNotexist مشتق شده است. بروز objectDoesNotexist در get_object()تولید یک خطای 404 برای آن درخواست را به جنگو می گوید. برای تولید <title>، <link> و <description> مربوط به feed، جنگو متدهای title()، link() و Description() را مورد استفاده قرار می دهد. در مثال قبلی، آن ها attribute های ساده ی کلاس رشته بودند، ولی این مثال نشان می دهد که، آن ها می تواند هم رشته و هم متد باشند. برای هر title، link و description، جنگو الگوریتم زیر را دنبال می کند: سعی می کند یک متد را فراخوانی کند، ارسال آرگومان obj، جایی که obj شیءی می باشد که توسط get_object() بر گردانده شده است. در صورت شکست، جنگو تلاش می کند یک متد با هیچ آرگومانی را فراخوانی کند. در صورت شکست، جنگو attribute کلاس را استفاده می کند. در پایان، توجه داشته باشید که items() در این مثال همچنین آرگومان obj را نیز دریافت می کند. الگوریتم برای items همانند الگوریتم توضیح داده شده در گام قبلی می باشد – ابتدا، سعی می کند items(obj)، سپس items() و در پایان یک attribute کلاس items (که باید یک لیست باشد). مستندات کامل از تمام متدها و attribute ها از کلاس های Feed همواره از اسناد رسمی جنگو قابل دسترسی می باشد ([Hidden Content]) تعیین نوع Feed به طور پیشفرض، فریم ورک syndication، RSS 2.0 را تهیه می کند. برای تغییر آن، یک attribute، feed_type برای کلاس Feed خودتان اضافه کنید: from django.utils.feedgenerator import Atom1Feed class MyFeed(Feed): feed_type = Atom1Feed توجه داشته باشید که feed_type را برای یک شیء کلاس در نظر گرفته اید، نه یک نمونه، در حال حاضر انواع feed های در دسترس در جدول زیر نشان داده شده اند. کلاس Feed Format Django.utils.feedgenerator.Rss201rev2Feed RSS 2.01 (default) Django.utils.feedgenerator.RssUserland091Feed RSS 0.91 Django.utils.feedgenerator.Atom1Feed Atom 1.0 Enclosures برای تعیین enclosure ها (مانند منابع media مرتبط با یک آیتم feed مانند feed های MP3 podcast)، item_enclosure_url، item_enclosure_length و item_enclosure_mime_type را استفاده کنید، به عنوان مثال: from myproject.models import Song class MyFeedWithEnclosures(Feed): title = "Example feed with enclosures" link = "/feeds/example-with-enclosures/" def items(self): return Song.objects.all()[:30] def item_enclosure_url(self, item): return item.song_url def item_enclosure_length(self, item): return item.song_length item_enclosure_mime_type = "audio/mpeg" البته که شما یک شیء Song با فیلدهای song_url و song_length (مانند اندازه ی بایت ها) ساخته اید. Language Feed های ساخته شده توسط فریم ورک syndication به طور خودکار شامل تگ مناسب <language> (RSS 2.0) یا attribute، xml:lang (Atom) می باشند. این به طور مستقیم از تنظیم LANGUAGE_CODE آمده است. URLs لینک متد/attribute می تواند هم یک URL مستقل (مانند "/blog/") یا یک URL با آدرس کامل دامنه و پروتکل (مانند "[Hidden Content]") باشد. در صورتی که link دامنه را بر نگرداند، فریم ورک syndication دامنه ی سایت فعلی را به همراه تنظیم SITE_ID درج خواهد کرد. (برای اطلاعات بیشتر در مورد SITE_ID و چارچوب سایت ها به پکیج django.contrib مراجعه کنید.) feed های Atom نیازمند یک <link rel="self"> می باشند که مکان فعلی feed را تعریف کند. فریم ورک syndication به طور خودکار این را قرار می دهد. انتشار feed های Atom و RSS پشت سر هم برخی از توسعه دهندگان تمایل دارند، فیلد های هر دو نسخه ی Atom و RSS در دسترس قرار دهند. انجام این کار با جنگو بسیار آسان می باشد: تنها کافیست یک کلاس فرزند از کلاس feed خود ایجاد کرده و feed_type را برای چیزی متفاوت قرار دهید. سپس URLconf خود را جهت اضافه کردن نسخه های اضافه به روز رسانی کنید. در زیر یک مثال کامل را ملاحظه می کنید: from django.contrib.syndication.feeds import Feed from django.utils.feedgenerator import Atom1Feed from mysite.blog.models import Entry class RssLatestEntries(Feed): title = "My Blog" link = "/archive/" description = "The latest news about stuff." def items(self): return Entry.objects.order_by('-pub_date')[:5] class AtomLatestEntries(RssLatestEntries): feed_type = Atom1Feed و در زیر URL همراه: from django.conf.urls.defaults import * from myproject.feeds import RssLatestEntries, AtomLatestEntries feeds = { 'rss': RssLatestEntries, 'atom': AtomLatestEntries, } urlpatterns = patterns('', # ... (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}), # ... ) فریم ورک یا چارچوب نقشه ی سایت نقشه ی سایت یک فایل XML در وب سایت شما می باشد که به indexer های موتور جستجو نحوه ی تغییر مکرر و نحوه ی ارتباط برخی صفحات مهم با دیگر صفحات سایت شما را می گوید. این اطلاعات به موتورهای جستجوی فهرست سایت شما کمک می کند. به عنوان مثال، در زیر یک تکه از نقشه ی سایت برای وب سایت جنگو وجود دارد ([Hidden Content]): <?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="[Hidden Content]; <url> <loc>[Hidden Content]; <changefreq>weekly</changefreq> <priority>0.5</priority> </url> <url> <loc>[Hidden Content]; <changefreq>never</changefreq> <priority>0.1</priority> </url> ... </urlset> برای مشاهده ی نقشه ی سایت های بیشتر به [Hidden Content] مراجعه کنید. فریم ورک نقشه ی سایت جنگو، ساختن این فایل XML را با اجازه دادن به شما جهت بیان این اطلاعات در کد پایتون خودکار می کند. جهت ساختن یک نقشه ی سایت، تنها نیاز به نوشتن یک کلاس Sitemap و اشاره ی به آن درون URLconf می باشد. نصب جهت نصب برنامه ی نقشه ی سایت، مراحل زیر را دنبال کنید: 'django.contrib.sitemaps' را به تنظیم INSTALLED_APPS اضافه کنید. 'django.template.loaders.app_directories.load_template_source'باید در تنظیم TEMPLATE_LOADERS وجود داشته باشد. این حالت به طور پیشفرض وجود دارد، بنابراین در صورتی که این تنظیم را تغییر داده اید، تنها کافیست آن را به حالت اول خود بر گردانید. اطمینان حاصل کنید که سایت های چارچوب را نصب کرده اید (به پکیج django.contrib مراجعه کنید). نکته برنامه ی نقشه ی سایت در هر جدول دیتابیسی نصب نمی شود. تنها دلیلی که برای رفتن به داخل INSTALLED_APPS نیاز دارد این است که template loader مورد نظر یعنی load_template_source بتواند template های پیشفرض را پیدا کند. مقدار دهی اولیه جهت فعال کردن تولید نقشه ی سایت در سایت جنگو، خط زیر را درون URLconf اضافه کنید: (r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}) خط فوق برای ساختن یک نقشه ی سایت هنگامی که یک کلاینت به /sitemap.xml دسترسی پیدا می کند در نظر گرفته شده است. توجه داشته باشید که حرف نقطه در sitemap.xml با یک علامت ("\") میسر شده است، زیرا نقطه ها معنی خاصی در regular expression ها دارند. نام فایل نقشه ی سایت اهمیتی ندارد، ولی مکان آن مهم می باشد. موتورهای جستجو تنها لینک ها را در نقشه ی سایت شما برای سطح URL فعلی و پایین فهرست می کنند. برای مثال، در صورتی که sitemap.xml در دایرکتوری ریشه ی شما می باشد، ممکن است به هر URL ای در سایت شما رجوع کند. در صورتی که نقشه ی سایت در /content/sitemap.xml باشد، ممکن است تنها به URL هایی که با /content/ شروع می شوند رجوع کند. view نقشه ی سایت یک آرگومان اضافه ی الزامی دریافت می کند: {'sitemaps': sitemaps}. نقشه های سایت باید یک دیکشنری باشند که بخش کوتاه لیبلی (مانند blog یا news) را به کلاس Sitemap آن (مانند BlogSitemap یا NewsSitemap) مرتبط می سازد. همچنین ممکن است به یک instance از یک کلاس Sitemap (مانند BlogSitemap(some_var)) مرتبط سازد. کلاس های Sitemap یک کلاس Sitemap یک کلاس ساده ی پایتون می باشد که یک بخش از ورودی های نقشه ی سایت شما را نشان می دهد. برای مثال، یک کلاس Sitemap هنگامی که دیگری می تواند تمام رخدادها در در رخدادهای تقویم نشان دهد تمام ورودی های وبلاگ شما را نشان دهد. در ساده ترین مورد، تمام این بخش ها با یکدیگر داخل یک sitemap.xml بدست می آیند، ولی استفاده از فریم ورک برای تولید یک فهرست نقشه ی سایت که به فایل های نقشه ی سایت منحصر به فرد نیز ممکن است، یکی برا هر بخش. کلاس های Sitemap باید از کلاس django.contrib.sitemaps.Sitemap مشتق شوند. آن ها می توانند هر جایی در درخت کد پایتون شما قرار بگیرند. برای مثال، فرض می کنیم شما دارای یک سیستم بلاگ می باشید، با یک مدل Entry، و می خواهید نقشه ی سایت تمام لینک ها به ورودی منحصر به فرد بلاگ را شامل شود. در زیر کلاس Sitemap مورد نظر وجود دارد: from django.contrib.sitemaps import Sitemap from mysite.blog.models import Entry class BlogSitemap(Sitemap): changefreq = "never" priority = 0.5 def items(self): return Entry.objects.filter(is_draft=False) def lastmod(self, obj): return obj.pub_date declare کردن نقشه ی سایت باید بسیار شبیه به یک Feed باشد. در دست ترجمه/تالیف ... همانند کلاس های Feed، اعضای Sitemap می توانند متد یا attribute ها باشند. به بخش کمی قبل در این آموزش از کتاب، "یک مثال پیچیده" برای نحوه ی انجام این اعمال مراجعه کنید. یک کلاس نقشه ی سایت می تواند متد/attribute های زیر را تعریف کند: items(required): لیستی از شیء ها تهیه می کند. فریم ورک نوع شیء هایی که هستند را زیر نظر ندارد؛ تمام موضوع این است که این شیء ها به متدهای location()، lastmod()، changefreq() و priority() ارسال شده باشند. location(optional): URL مستقل برای شیء داده شده را می دهد. در اینجا، "URL مستقل" به معنی یک URL می باشد که شامل پروتکل یا دامنه نباشد. تعدادی مثال را در زیر مشاهده می کنید: خوب: '/foo/bar/' بد: 'example.com/foo/bar/' بد: '[Hidden Content]' در صورتیکه location تهیه نشده باشد، فریم ورک متد get_absolute_url() را در هر شیء به صورت برگشت داده شده توسط items() فراخوانی خواهد کرد. lastmod(optional): شیء های "آخرین اصلاح" زمان و تاریخ، به صورت یک شیء datetime پایتون. changefreq(optional): هر چند وقت یک بار شیء را تغییر می دهد. مقادیر ممکن (به صورت داده شده توسط تعیین نقشه های سایت) به قرار زیر می باشند: 'always' 'hourly' 'daily' 'weekly' 'monthly' 'yearly' 'never' priority(optional): یک پیشنهاد اولویت فهرست سازی بین 0.0 و 1.0. اولویت پیشفرض یک صفحه 0.5 می باشد؛ برای اطلاعات بیشتر درباره ی عمکرد اولویت به [Hidden Content] مراجعه کنید. میانبرها فریم ورک نقشه ی سایت کلاس های مناسبی برای موارد مشترک تهیه کرده است. این کلاس ها در بخش های زیر توضیح داده شده اند. FlatPageSitemap کلاس django.contrib.sitemaps.FlatPageSitemap به تمام صفحات مسطح تعریف شده برای سایت فعلی نگاه می کند و یک ورودی در نقشه ی سایت ایجاد می کند. این ورودی ها تنها شامل attribute مورد نظر یعنی location می شوند – نه lastmod، changefreq یا priority. GenericSitemap کلاس GenericSitemap با هر view جنریکی کار می کند که شما آن ها را فرا گرفته اید. برای استفاده از آن، یک نمونه بسازید، ارسال در info_dict یکسان که شما به view های جنریک ارسال می کنید. تنها نیازمندی این است که دیکشنری یک ورودی queryset داشته باشد. ممکن است همچنین یک ورودی date_field داشته باشد که یک فیلد تاریخ برای شیء های بازیابی شده از queryset را تعیین می کند. این برای attribute، lastmode در نقشه ی سایت تولید شده مورد استفاده قرار خواهد گرفت. شما ممکن است آرگومان های کیورد priority و changefreq را برای سازنده ی GenericSitemap جهت تعیین این attribute ها برای همه ی URL ها ارسال کنید. در زیر مثالی از یک URLconf که از هر دوی FlatPageSitemap و GenericSiteMap (با شیء فرضی Entry از پیش) استفاده می کند وجود دارد: from django.conf.urls.defaults import * from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap from mysite.blog.models import Entry info_dict = { 'queryset': Entry.objects.all(), 'date_field': 'pub_date', } sitemaps = { 'flatpages': FlatPageSitemap, 'blog': GenericSitemap(info_dict, priority=0.6), } urlpatterns = patterns('', # some generic view using info_dict # ... # the sitemap (r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}) ) ساخت فهرست نقشه ی سایت فریم ورک نقشه ی سایت همچنین دارای توانایی برای ساختن یک فهرست نقشه ی سایت می باشد که به فایل های منحصر به فرد نقشه ی سایت رجوع می کند، یکی برای هر بخش تعریف شده در دیکشنری sitemaps. تنها تفاوت ها در کاربرد هستند: شما از دو view در URLconf خود استفاده می کنید: django.contrib.views.index و django.conrib.sitemaps.views.sitemap. django.contrib.sitemaps.views.sitemap باید یک آرگومان کیورد section دریافت کند. در زیر خطوط URLconf مربوط برای مثال قبلی وجود دارد: (r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', {'sitemaps': sitemaps}), (r'^sitemap-(?P<section>. ).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}) کد فوق به طور اتوماتیک یک فایل sitemap.xml تولید می کند که به هر دوی sitemap-flatpages.cml و sitemap-blog.xml رجوع می کند. کلاس های Sitemap و دیکشنری sitemaps هیچگاه تغییر نمی کنند. پینگ کردن گوگل ممکن است بخواهید گوگل را هنگامی که نقشه ی سایت شما تغییر می کند پینگ کنید، تا به آن اجازه دهید دوباره فهرست گذاری سایت شما را بداند. چارچوب یک تابع تنها برای فقط برای این کار تهیه کرده است: django.contrib.sitemaps.ping_google(). ping_google() یک آرگومان اختیاری دریافت می کند، sitemap_url، که باید URL مستقل از نقشه ی سایت سایت شما باشد (مانند '/sitemap.xml'). در صورتی که آرگومان تهیه نشده باشد، ping_google() تلاش می کند نقشه ی سایت شما را توسط انجام یک جستجوی بر عکس در URLconf بسنجد. ping_google() در صورتی که نتواند URL نقشه ی سایت را تعیین کند، خطای django.contrib.sitemaps.SitemapNotFound را ایجاد خواهد کرد. روش مفید برای فراخوانی ping_google به شکل یک متد save() مدل می باشد: from django.contrib.sitemaps import ping_google class Entry(models.Model): # ... def save(self, *args, **kwargs): super(Entry, self).save(*args, **kwargs) try: ping_google() except Exception: # Bare 'except' because we could get a variety # of HTTP-related exceptions. pass راهکار موثر تر، فراخوانی ping_google() از یک اسکریپت cron یا برخی وظایف برنامه ریزی شده ی دیگر می باشد. تابع یک HTTP request برای سرور های گوگل ایجاد می کند، بنابراین شما ممکن نیست بخواهید برای معرفی در هر بار فراخوانی save() بار اضافه ی شبکه را داشته باشید. در پایان، در صورتی که 'django.contrib.sitemaps' درون تنظیم INSTALLED_APPS می باشد، manage.py شما شامل یک دستور جدید، ping_google خواهد بود. این برای دسترسی به پینگ کردن درون خط فرمان مفید می باشد. برای مثال: python manage.py ping_google /sitemap.xml
  14. مقاله عناصر و کیفیت های منظر یک فضای شهری نوشته جهانشاه پاکزاد که در شماره سی و هشتم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. عناصر و کیفیت های منظر یک فضای شهری.pdf
  15. مقاله اصول و مبانی طراحی و ساماندهی حوزه نفوذ آزادراه های کشور نوشته اصغر ارجمندنیا که در شماره سی و هشتم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. اصول و مبانی طراحی و ساماندهی حوزه نفوذ آزادراه های کشور.pdf
  16. مقاله توجه به معیارهای طراحی شهری در انتخاب سایت برج مخابراتی تهران نوشته محمدرضا پورجعفر که در شماره سی و هشتم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. توجه به معیارهای طراحی شهری در انتخاب سایت برج مخابراتی تهران.pdf
  17. بخش دوم از معرفی پروژه های معماری معاصر کشورهای اسلامی ترجمه مسعود پرچمی عراقی که در شماره سی و هشتم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. معرفی پروژه های معاصر کشورهای اسلامی.pdf
  18. Mohammad Aref

    معماری مدرن

    مقاله مروری بر معماری مدرن خاورمیانه نوشته نوشته پیتر دیوی و ترجمه مرضیه بانژاد که در شماره سی و هشتم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. مروری بر معماری مدرن خاورمیانه.pdf
  19. Mohammad Aref

    مسجد، سمبل شهر اسلامی

    مقاله مسجد در شهر نوشته اکبر حاجی ابراهیم زرگر که در شماره سی و هشتم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. مسجد در شهر.pdf
  20. Mohammad Aref

    معماری معاصر

    مقاله خانه های معاصر، ویژگی ها و نیازها نوشته محمدرضا حائری که در شماره سی و هشتم مجله آبادی منتشر شده است را به صورت PDF از پیوست می تونید دانلود کنید. خانه های معاصر، ویژگی ها و نیازها.pdf
  21. سلام. مواردی که خواستین تو طرح شهرسازی 5 معمولاً آموزش داده میشه که تو لینک زیر هم هست:
  22. احتمالا کلید اشتباه بوده. این اشتباهات تو ازمونها معمولا هست.
×
×
  • جدید...