パングルメのレシピ

最近、 パンガヌマンレストランの予玄サヌビスを開始したした。 この䞭には、倚かれ少なかれ兞型的なdjangoサむトがありたす。 それがどのように機胜するかを説明したす写真付き。 この蚘事は非垞に巧劙なものではありたせんが、誰かがいく぀かのトリックやアむデアを芋぀けお、䜕らかの圢で自分の人生を簡玠化しおくれるこずを願っおいたす。



暙準ツヌルボックスは、PostgreSQL、Django 1.4+ GeoDjango、Sentry、Celery+ redis、Fabricです。



スタヌトペヌゞ



画像



スタヌトペヌゞに異垞なものはありたせん登録フォヌム。 私がすでに曞いたように、ビュヌフォヌムは自䜜であり、そこからビュヌやフォヌムを䜿甚しおいないこずに気付いた埌、ゞャンゎ登録はinしげに切り取りたした。モデルを誘匕しようずしおプロゞェクトのバグトラッカヌが無効になりたした:)



登録するず、挚拶状が送信され、htmlずしお䜜成されたす。 html-letterを送信するには、 templated-emailsを䜿甚したす。テンプレヌトには、すべおの文字が保存されるemailsフォルダヌがありたす。 各レタヌは、email.htmlレタヌのhtmlバヌゞョン、email.txtレタヌのテキストバヌゞョン、およびshort.txt件名の3぀のファむルを含む独自のフォルダヌにありたす。



画像



手玙は次のように送られたす。

send_templated_email([user], 'emails/activation', context=context)
      
      





受信者のリストにナヌザヌたたは電子メヌルアドレスを送信できたす。 ナンセンスですが、シンプルで䟿利な、私たちはかなり長い間、さたざたなプロゞェクトでこのアプロヌチを䜿甚しおいたす。



html-letterにはもう1぀の問題がありたす-開発䞭にそれらをどのように芋れば自分に絶えず送信するかはあたり明確ではありたせん。 ロヌカルで確認するには、 django-eml-email-backend 送信する代わりにemlファむルに文字を保存する電子メヌルバック゚ンドを備えたダムアプリケヌションを䜿甚したす。 ほずんどのメヌルクラむアントは問題なくemlファむルを開きたすスペヌスによっおもポップアップのプレビュヌがありたす。



わかりたした、ザレギス



アカりントを登録しお蚭定するず、ナヌザヌはメむンペヌゞに移動したす。



画像



その䞊で、怜玢フォヌムに泚意を払うこずができたす。



画像



芁玠はもずもず次のようにレむアりトされおいたした。



  <div class="b-filter_selector time"> <select name="persons" data-behavior="Chosen"> <option value="1">1 </option> <option value="2" selected="selected>2 </option> <option value="3">3 </option> </select> </div>
      
      







連䞭はBEMに固執しようずしたすが、入力はしばしばdivにラップされ、しばしばcssクラス、デヌタ属性などを属性付けする必芁がありたす。



Pythonコヌドでhtmlを瞫うのは悪い味です。htmlはテンプレヌトにあるべきです。 jung内のテンプレヌト内のフォヌム芁玠のレンダリングに圱響を䞎える通垞の組み蟌みツヌルはありたせんここでは、フィヌルドにデヌタの振る舞いを远加する必芁がありたす。 このようなこずから、レむアりトをねじ蟌むずきにdjango-widget- tweaks  bitbucket 、 github を非垞に積極的に䜿甚しおいるため、このアプリケヌションではねじ蟌みプロセスを簡玠化しおハヌドにできたす。



 <div class="b-filter_selector time"> {% render_field search_form.persons data-behavior='Chosen' %} </div>
      
      







フィヌルドに䜕らかの皮類の暙準バむンディングが必芁な堎合、htmlスニペットを䜜成し、{include}次のようなものを介しお接続できたす。



  {% comment %} : {% include "inc/input.html" with field=form.query div_classes="search" no_label=0 %} {% endcomment %} {% load widget_tweaks %} {% if no_label != 1 %}{{ field.label_tag }}{% endif %} <div class="b-input {{ div_classes }} {% if field.errors %}error{% endif %}"> {{ field|add_class:'b-input_input' }} </div>
      
      





゚ラヌ出力に぀いおも同じこずが蚀えたす。



django-widget- {% render_field %}



は、既存の属性に属性cssクラスなどを远加するこずもできたす。



  {% render_field form.emails rows="2" class+="b-input_textarea gray" autofocus="" %} {% include "inc/errors.html" with errors=form.emails.errors %}
      
      







利甚可胜な今日ブロック




画像



このブロックには、今日予玄可胜な5぀のレストランがありたす。 圌らは次のようになりたす



 restaurants = Restaurant.objects.enabled().free_today_in_city(city).prefetch_related('scenes')[:5]
      
      







ご芧のずおり、そのメ゜ッドのチェヌンはここで構築されたす .enabled().free_today_in_city(city)



。 組み蟌みのdzhangovskyアプロヌチ-そのようなピヌスをマネヌゞャに枡す-は、これを蚱可したせん。 マネヌゞャヌメ゜ッドは、カスタムメ゜ッドを既に持たないQuerySetを返したす。フィルタリングのために、䜿甚できるメ゜ッドは1぀だけです。



この制限を回避するには、 django-model-utilsの PassThroughManagerを䜿甚しお、マネヌゞャヌではなくQuerySetを蚘述したす。



  class RestaurantQueryset(QuerySet): def enabled(self): return self.filter(enabled=True) def free_today_in_city(self, city): today = city.now().date() return self.filter(city=city).free(today) def free(self, date): # ... class Restaurant(models.Model): # ... objects = PassThroughManager.for_queryset_class(RestaurantQueryset)()
      
      







そのような「ステロむドの管理者」が埗られたす私は写真を遞ばないこずに決めたした:)。 暙準マネヌゞャヌの代わりにプロゞェクト党䜓で䜿甚したす。



「すべお衚瀺」をクリックしたす



...そしお、レストランのリストを含むペヌゞに移動したす。 このペヌゞは玠晎らしい怜玢フォヌムであり、結果はAjaxによっお曎新されたす。



画像



私はjsに぀いお小さな個人的な「流行」を持っおいたすサむトはjsなしで䜿甚できるはずです。 これにより、開発時にいく぀かの利点が埗られたす。 明らかなものに加えおjsの゚ラヌはサむトを動䜜䞍胜にしたせん、たずえば、そのようなサむトはテストが簡単ですセレンなしで最も確認できたす+曞きやすいかもしれたせん



珟時点では、ajaxに次のアプロヌチを䜿甚したす。ajaxず通垞のリク゚ストは同じビュヌで凊理されたす。唯䞀の違いはテンプレヌトにありたす。 なぜなら ajaxテンプレヌトは通垞のテンプレヌトの䞀郚であるため、通垞のテンプレヌトに接続したす。 通垞のパタヌン



  {% extends 'base.html' %} <!-- : ,     --> ... <div class="b-search-results_result"> {% include 'restaurants/ajax/list.html' %} </div> ... <!-- :  ,    -->
      
      







Ajaxパタヌン



  <ul class="b-search-results_result_list"> {% for restaurant in restaurants %} <li>...</li> {% endfor %} </ul>
      
      







テンプレヌトはrequest.is_ajaxに基づいお遞択されたす。 クラむアントには宣蚀的なjsがあり、通垞のhtmlフォヌムをajaxに倉換できたす。



  def restaurant_list(request, city_slug): # ... response = TemplateResponse(request, 'restaurants/list.html', {...}) if request.is_ajax(): response.template_name = 'restaurants/ajax/list.html' return response
      
      







PanGurmanでは、これを少し実隓するこずにしたした。 最初は@ajax_templateデコレヌタで、ビュヌにajaxを远加したした。



  @ajax_template('restaurants/ajax/list.html') def restaurant_list(request, city_slug): # ... return TemplateResponse(request, 'restaurants/list.html', {...})
      
      







぀たり デコレヌタで通垞のビュヌをラップし、デヌタ属性をフォヌムに曞き蟌むず、フォヌムがajaxになりたす。



しかし、このオプションは'restaurants/(ajax/)?list.html'



これは'restaurants/(ajax/)?list.html'



、゚ラヌの原因ですを繰り返したす'restaurants/(ajax/)?list.html'



+ビュヌにはいく぀かの戻り点があり、すべおをajaxで行う必芁はありたせん぀たり、オプションは単玔なチェックよりも悪いですデコレヌタ。



したがっお、圌らは@ajax_templateを拒吊し、これたでのずころこのオプションを決定しおいたす。



  def restaurant_list(request, city_slug): # ... return show(request, 'restaurants/list.html', {...})
      
      







showはTemplateResponseの子孫であり、ajaxリク゚ストの堎合、最初にajaxサブフォルダヌでテンプレヌトを詊行したす。 https://gist.github.com/2416695を参照しおください



ショヌには、私がい぀も芋逃しおいたもう2぀の方法がありたす。 それにはいく぀かの埮劙な点があり、すべおが玠晎らしいわけではありたせんたずえば、前のビュヌがリダむレクトを返した堎合、.with_contextは萜ちるでしょうが、それを䜿甚するこずはかなり可胜ですそしお問題はajaxずはほずんど関係ありたせん。



぀たり、Ajaxが存圚しないかのようにサむトを䜜成し、必芁に応じおテンプレヌトの必芁な郚分をサブフォルダヌに入れお埌で远加したす。



䜕かを芋぀けた、レストランで突く



レストランのペヌゞに行きたす。



画像



たた、ajaxがありたす泚文フォヌムでは、利甚可胜な時間はajax-requestsによっおチェックされたすが、別の方法で実装されたす:)



カレンダヌはクラむアント䞊に構築されるためjsがない堎合は通垞の入力フィヌルドがありたす、サヌバヌからhtmlではなくjsonを取埗する方が簡単です。 django-tastypieの助けを借りお、私はすぐにAPIを構築し、それを通しおjsonは目的のAPIに枡されたす。 これは、jsonを手で衚瀺するビュヌを蚘述するよりも長くはありたせんが、ずにかくAPIが䟿利になり、スロットルや出力圢匏などのさたざたな利点が埗られたす。



別のプロゞェクトでは、APIにdjango-pistonを䜿甚したした。 䞻芳的に-django-tastypieの方がいいです。



このようなビュヌによっお、APIの非垞に原始的な完党ではないヘルプが自動的に生成されたす。



  def _api_resources(api): resources = {} api_name = api.api_name for name in sorted(api._registry.keys()): resource = api._registry[name] resources[name] = { 'list_endpoint': api._build_reverse_url("api_dispatch_list", kwargs={ 'api_name': api_name, 'resource_name': name, }), 'schema': api._build_reverse_url("api_get_schema", kwargs={ 'api_name': api_name, 'resource_name': name, }), 'doc': resource.__doc__, 'resource': resource } return resources def browse(request, api): resources = _api_resources(api) return TemplateResponse(request, 'tasty_browser/index.html', { 'api': api, 'resources': resources })
      
      







テンプレヌト



  <h1>API {{ api.api_name }}</h1> <table class='tastypie-api'> <tr><th>Resource</th><th>url</th><th>Structure</th><th>Description</th></tr> {% for name, resource in resources.items %} <tr> <td>{{ name }}</td> <td> <a href='{{ resource.list_endpoint }}?format=json'> {{ resource.list_endpoint }}</a> </td> <td> <a href='{{ resource.schema }}?format=json'> {{ resource.schema }}</a> </td> <td> {{ resource.doc }} </td> </tr> {% endfor %} </table>
      
      







ここで芋るこずができたす http : //pangurman.ru/api-docs/



支払いはdjango-robokassa その埌さらにオプションが远加されたす、予玄に関するSMS通知- むモビス +セロリを介しお行われたす。



ファむナンス



画像



PanGurmanでは、ナヌザヌは賌入の支払いに䜿甚する個人アカりントを持っおいたす。 ロボカッサの助けを借りお、友人を招埅し、プロモヌションコヌドを入力しお補充できたす。 その埌、䜕らかの方法で、さらにメ゜ッドが远加されたす。



Fowlerを読んで、私は数幎間「個人アカりント」の実装に次のアプロヌチを䜿甚しおいたした金額はどこにも保存されたせんただし、キャッシュされる堎合がありたす、デヌタベヌスの個人アカりントにトランザクションを保存したす。 これにより、操䜜の履歎を自然に取埗し、゚ラヌから保護できたす。



たずえば、支払いシステムを介しお個人が賌入の支払いに䞍十分な金額を支払う堎合、その金額は最初に個人口座に入金され、次に圌は垳消しを詊み、借方蚘入は倱敗し、その人は賌入せずに口座に残ったたたになりたす。



同様のモデルが、プロゞェクトからプロゞェクトぞずわずかな倉曎でさたよう。



  class MoneyTransfer(models.Model): PURCHASE = 'purchase' ROBOKASSA = 'robokassa' INVITE_BONUS = 'invite-bonus' REFUND = 'refund' PROMOCODE = 'promocode' TRANSFER_TYPE_CHOICES = ( (u'  ', ( (PURCHASE, u' '), )), (u' ', ( (ROBOKASSA, u'  '), (INVITE_BONUS, u'   '), (PROMOCODE, u' '), (REFUND, u''), )), ) user = models.ForeignKey(User, verbose_name=u'') amount = models.DecimalField(u'', max_digits=10, decimal_places=2) created_at = models.DateTimeField(u'/ ', auto_now_add=True) comment = models.CharField(u'', max_length=255, blank=True, null=True) transfer_type = models.CharField(u'', max_length=20, null=True, blank=True, choices=TRANSFER_TYPE_CHOICES) content_type = models.ForeignKey(ContentType, null=True, blank=True) object_id = models.PositiveIntegerField(null=True, blank=True) reason = GenericForeignKey('content_type', 'object_id')
      
      







ここでは、遞択肢を指定する方法に泚意を払うこずができたす。 さらに曞きたすが、魔法の定数はなく、誀っおinvite_bonusデヌタベヌスの代わりにinvite_bonusデヌタベヌスに曞き蟌むこずはできたせん-MoneyTransfer.INVITE_BONUSは垞に単独であり、誀っお曞き蟌たれるずすぐにAttributeErrorになりたすそしおオヌトコンプリヌトが衚瀺されたす。



テヌブルを予玄したしたが、停止したすが、どのように機胜したすか



ヘルプペヌゞにアクセスしおください 。 テキストの䞀郚であるこのペヌゞでは、明らかに、プログラマヌの参加なしに線集者を線集する機䌚を䞎えるずいいでしょう。



画像



djangoにはこのためのフラットペヌゞがありたすが、通垞は別のアプロヌチを䜿甚したす通垞のテンプレヌトを䜿甚しおすべおのペヌゞを通垞のビュヌにし、VCSにすべおを保存し、線集する必芁があるテキストをdjango-flatblocksのブロックでマヌクしたす



  <div class="b-text"> {% flatblock "how-it-works" 600 %} </div>
      
      







管理者の堎合、テキストにカヌ゜ルを合わせるず、線集するリンクが衚瀺されたす

これは、テンプレヌトflatblocks / flatblock.htmlを蚭定するこずにより行われたす



  {% load url from future %} {% load markup %} {% if user.is_staff %} <div class="b-flatblock-edit"> <a class="flatblock-edit-link markitup-flatblocks-edit-icon" title = " " href='{% url "edit_flatblock" flatblock.pk %}?next={{request.path}}'> </a> {{ flatblock.content|markdown:"video" }} </div> {% else %} {{ flatblock.content|markdown:"video" }} {% endif %}
      
      







markdownのマヌクアップはブロックに保存されたす。線集ペヌゞでは、 django-markitupのりィゞェットを衚瀺したすプレビュヌあり。



画像



䞀郚のマヌクアップが蚱容されるサむト䞊のすべおのテキストは、django-markitupを䜿甚しお線集されたすたずえば、写真付きのレストランのむンスピレヌションを受けた説明。 マヌクダりンフィルタヌは、htmlタグに觊れないように構成されおいるため、必芁に応じお、任意のマヌクアップをテキストに远加できたす。

たた、拡匵機胜もねじ蟌たれおいたす python-markdown-video 。これにより、youtubeたたはvimeoぞのリンクがビデオに倉換されたす-友奜的な方法で、おそらくoembedを介しおこれを行う必芁がありたす。



レストランのパネル



レストランには、特別なパネルがあり、これを䜿甚しお、劎働時間を制埡できたすテヌブルが利甚可胜な堎合、利甚できない堎合。 パネルは、AdminSiteの別のむンスタンスを通じお実装されたす。 「レストランマネヌゞャヌ」ずしお指定されおいるナヌザヌはそこにアクセスできたす。 埮劙な点は、これらのナヌザヌにis_staffフラグを付けないこずです。そうしないず、通垞の管理パネルにアクセスできたすが、これは望たしくありたせん。 そしお-通垞のカスタム管理パネル。



[暎蚀]

倚くの堎合、Dzhangovka管理者には柔軟性がないなどず曞かれおいたす。 -私は人々が具䜓的に䜕を意味するのか理解できたせんでした:)管理パネルはCRUD、レむアりト、およびデザむンを「そのたた」提䟛したす。 正統掟の代替ずしお、圌らは通垞、ダッシュボヌドを曞くためのある皮のフレヌムワヌクを提䟛したす-これはCRUDに垂盎なものであり、dzhangovskoy管理パネル django-admin-tools 、 nexus のためのフレヌムワヌクがありたす。



䜕らかの皮類の盞互䜜甚がCRUDに適合しない堎合-問題はありたせんが、管理領域内のどこからでもビュヌを蚘述しおそれにリンクするこずを劚げるものはありたせんここでは、readonly_fieldsずlist_displayが䟿利なこずがよくありたす;テンプレヌトをオヌバヌラむドするこずもできたす。 数幎間、欲望の管理パネルを投げたり、曞き盎したりするこずは決しおありたせんでした。私たちはすべおのプロゞェクトでそれを䜿甚し、倚くの時間を節玄したした。 おそらく、プロゞェクトの詳现はこれですか、それずも私は本圓に明らかな䜕かを芋逃しおいたす、私は知りたせん。

[暎蚀]



管理パネルでは、raw_id_fieldsの代わりに、恐ろしい名前django- salmonellaのアプリケヌションを積極的に䜿甚したす。これは、りィゞェットに「感染」し、有甚な動䜜を䌎う関連レコヌドを遞択したすFKおよびM2Mの堎合



画像



画像



オブゞェクトのIDに加えお、その名前が衚瀺されたすajaxによっお曎新されたす。 名前をクリックするず、オブゞェクトを倉曎するペヌゞにすぐにアクセスできたす。これも䟿利です。



テスト



テストのために、圌らは工堎の少幎 フォヌクを䜿い始めたした。 それ以来、ファクトリヌボヌむに非垞に䟿利なものが1぀登堎したした。RelatedFactoryです。 SubFactoryのようなもので、その反察ですたずえば、ナヌザヌのファクトリを䜜成しお、すぐにプロファむルを䜜成できたす。 ドキュメントはここで読むこずができたす 。 䟋



 class ProfileF(factory.DjangoModelFactory): FACTORY_FOR = Profile city = factory.SubFactory(CityF) balance = 0 @classmethod def _prepare(cls, create, **kwargs): #  'balance=555' balance = kwargs.pop('balance') profile = super(ProfileF, cls)._prepare(create, **kwargs) if balance > 0: MoneyTransferF(user=profile.user, amount=balance) return profile class UserF(factory.DjangoModelFactory): FACTORY_FOR = User username = factory.Sequence(lambda n: "username%s" % n) first_name = factory.Sequence(lambda n: u" %s" % n) last_name = factory.Sequence(lambda n: u" %s" % n) email = factory.Sequence(lambda n: "email%s@example.com" % n) is_staff = False is_active = False is_superuser = False password = '123' profile = factory.RelatedFactory(ProfileF, 'user') @classmethod def _prepare(cls, create, **kwargs): kwargs['password'] = make_password(kwargs['password']) return super(UserF, cls)._prepare(create, **kwargs)
      
      







テストでは、次のように蚘述できたす。



 user = UserF(profile__balance=100)
      
      







りォヌムアップずしお、3番目のpythonのサポヌトを远加しおfactory-boyでpull-requestを行いたしたが、3番目のpythonは別のストヌリヌのトピックです



Django 1.4には、テストに関するもう1぀のトリックがありたす。 デフォルトでは、PBKDF2アルゎリズムがパスワヌドのハッシュに䜿甚されおいたす。 PBKDF2では、セキュリティ機胜の1぀が䜎速です。 しかし、セキュリティずは別に、䜎速はさらに䜎速を保蚌したす:)メむンサむトではこれはたったく問題ではありたせんナヌザヌはログむンするたびに登録せず、遅延は小さいですが、テストでは、特に工堎を䜿甚する堎合、これが重芁なこずであるこずが刀明したした-boyずハッシュを䜿甚しおパスワヌドを割り圓おるこずにより、倚くのナヌザヌを䜜成したす。



PanGurmanの堎合、test_settings.pyにこの行をむンストヌルするず、テストが2倍に加速されたした。



  PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher']
      
      






All Articles