diff --git a/README.rst b/README.rst index ed9b11c975ef86621b091d908534e525cdaf024f..4498cf538738c0adc7314d5a13dfb98da747c344 100644 --- a/README.rst +++ b/README.rst @@ -31,14 +31,14 @@ Installation: You can install it with one of these options: -- easy\_install django-menu-generator -- pip install django-menu-generator -- git clone https://github.com/RADYConsultores/django-menu-generator +- ``easy_install django-menu-generator`` +- ``pip install django-menu-generator`` +- ``git clone https://github.com/RADYConsultores/django-menu-generator`` - 1. cd django-menu-generator + 1. ``cd django-menu-generator`` 2. run python setup.py -- wget https://github.com/RADYConsultores/django-menu-generator/zipball/master +- ``wget https://github.com/RADYConsultores/django-menu-generator/zipball/master`` 1. unzip the downloaded file 2. cd into django-menu-generator-\* directory diff --git a/docs/_build_html/.doctrees/changelog.doctree b/docs/_build_html/.doctrees/changelog.doctree index b2d55c6ce02adf6da61aac4d85dc9d5089eb7991..ccaac60189bb31dc23d55ba3297b021313e478b2 100644 Binary files a/docs/_build_html/.doctrees/changelog.doctree and b/docs/_build_html/.doctrees/changelog.doctree differ diff --git a/docs/_build_html/.doctrees/environment.pickle b/docs/_build_html/.doctrees/environment.pickle index 6e6049c00d3c5e43b2d529db4052baf17a1bb06a..f7723c50a5903ec7eab9c4c4da9adae298f5c36c 100644 Binary files a/docs/_build_html/.doctrees/environment.pickle and b/docs/_build_html/.doctrees/environment.pickle differ diff --git a/docs/_build_html/.doctrees/menugeneration.doctree b/docs/_build_html/.doctrees/menugeneration.doctree index 8dd4960f7317042ff529f80fa06c572c77bf5c36..21626c8ad0acb84fca406fded570266372947f07 100644 Binary files a/docs/_build_html/.doctrees/menugeneration.doctree and b/docs/_build_html/.doctrees/menugeneration.doctree differ diff --git a/docs/_build_html/.doctrees/urls.doctree b/docs/_build_html/.doctrees/urls.doctree index 94527770bb220113e0ca8de8d36fcfab903d4e19..6c0c8324a768c006496629fce7fc9932befcce5d 100644 Binary files a/docs/_build_html/.doctrees/urls.doctree and b/docs/_build_html/.doctrees/urls.doctree differ diff --git a/docs/_build_html/.doctrees/validators.doctree b/docs/_build_html/.doctrees/validators.doctree index d2f609e7edaca3d6181187a750bc8d86cdcd5685..e1c7c24e0fcd8454e5697b81d92442a9e0d9b19d 100644 Binary files a/docs/_build_html/.doctrees/validators.doctree and b/docs/_build_html/.doctrees/validators.doctree differ diff --git a/docs/_build_html/_sources/changelog.rst.txt b/docs/_build_html/_sources/changelog.rst.txt index c4df4750f73b208f7e0981d82a71606e2bed26e4..33a4f832eef17824a9098535a13c11c3aa69586d 100644 --- a/docs/_build_html/_sources/changelog.rst.txt +++ b/docs/_build_html/_sources/changelog.rst.txt @@ -1,2 +1,16 @@ CHANGELOG ========= + +1.0.1(2017-04-29) +----------------- + +- Added docs +- Readme enhanced +- BUGFIX: Added a correction to the validators evaluation with the AND connector +- Added flake8 to CI +- Added tox + +1.0.0 (2017-04-09) +------------------ + +- Initial release \ No newline at end of file diff --git a/docs/_build_html/_sources/menugeneration.rst.txt b/docs/_build_html/_sources/menugeneration.rst.txt index f1426f797bbbfe777838599534b5295b38f74420..23e2846d55320072304b383f27fcac74dd6ef515 100644 --- a/docs/_build_html/_sources/menugeneration.rst.txt +++ b/docs/_build_html/_sources/menugeneration.rst.txt @@ -1,2 +1,86 @@ Menu Generation =============== + +Django Menu Generator uses python dictionaries to represent the menu items, usually a menu item is as follows: + +.. code:: python + + { + "name": 'some name', + "icon_class": 'some icon class', + "url": URL spec, + "validators": [ list of validators ], + "submenu": Dictionary like this + } + +Where each key is as follows: + +- ``name``: A string representing the label of the menu item. If you are using i18n here you can pass the name with the ``ugettext_lazy`` function + +- ``icon_class``: A string representing the class of the icon you wish to show on the menu item, e.g you can use font-awesome + +- ``url``: See :doc:`urls` + +- ``validators``: See :doc:`validators` + +- ``submenu``: You can create infinite nested submenus passing here menu items like this + +Django Menu Generator offers two ways to generate the menus, through the Django settings and through each of the Django +apps + +Generating menus through settings +--------------------------------- + +You can add various list dictionaries representing each menu you have as explained in :doc:`usage` +We recommend to have a ``menus.py`` file with the menu list dictionaries and then import it to the settings file if you +go this way + +Generating menus through apps +----------------------------- + +Some people prefer to isolate all the aspects of a project between apps, so, we add this feature to allow the menus +live inside each app. + +You need to add inside the app a ``menus.py`` file that contains a dictionary called ``MENUS``, each element of the +dictionary will be a menu list dictionary with all the configuration needed to display that menu, e.g: + +.. code:: python + + MENUS = { + 'NAV_MENU_LEFT': [ + { + "name": "App1 Feature", + "url": "/app1-feature" + } + ], + 'NAV_MENU_TOP': [ + { + "name": "Second Menu Feature", + "url": "named_url" + } + ] + } + +So, as an example, for the ``'NAV_MENU_LEFT'``, Django Menu Generator will loop each app searching for the ``'NAV_MENU_LEFT'`` +list dictionaries inside of the ``MENUS`` and build all the menu configuration to build the whole menu. + +With this feature you can have a project structure like this:: + + your_project/ + ├── config_folder/ + │ └── ... + ├── app1 + │ └── models.py + │ forms.py + │ views.py + │ menus.py + │ + ├── app2 + │ └── models.py + │ forms.py + │ views.py + │ menus.py + │ + ... + +You can have a mix of the two approaches if you wish \ No newline at end of file diff --git a/docs/_build_html/_sources/urls.rst.txt b/docs/_build_html/_sources/urls.rst.txt index b5d18e70783d30cbc8de7728036d130af5452890..c7e4e8d43aaa53e262a4c525e232c87cf8af8406 100644 --- a/docs/_build_html/_sources/urls.rst.txt +++ b/docs/_build_html/_sources/urls.rst.txt @@ -1,2 +1,40 @@ URLs ==== + +You can pass the URL parameters to menu items in three ways. + +Raw URLs +-------- + +A hard-coded url: + +.. code:: python + + "url": '/some-path/to-feature' + +Reversible URLs +--------------- + +An url that can be reversed with the `reverse` method: + +.. code:: python + + "url": 'named_url' + +URL with args or kwargs +----------------------- + +e.g. If you have an url with kwargs like this: + +.. code:: python + + url(r'^update/(?P<pk>\d+)/$', SomeView.as_view(), name='update'), + +you can pass the url as follows: + + "url": {"viewname": 'update', "kwargs": {"pk": 1}} + +In fact, you can pass any of the parameters of the reverse method through the dictionary + +For Django 1.10 the reverse method sign is: ``reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)`` + diff --git a/docs/_build_html/_sources/validators.rst.txt b/docs/_build_html/_sources/validators.rst.txt index 70fbe5759b5eb4d64d8203f7bb1a83d2057deeaf..839126a9e2eee5b6da9eba713cb2ba6ae1238cf8 100644 --- a/docs/_build_html/_sources/validators.rst.txt +++ b/docs/_build_html/_sources/validators.rst.txt @@ -1,2 +1,123 @@ Validators ========== + +Django Menu Generator uses validators to allow the displaying of menu items. + +A validator is a function that receives the request as arg and returns a boolean indicating if the check has passed + +for Django Menu Generator the validators must always be a list containing at least one callable or python path to a callable. +If there is more than one validator, all of them will be evaluated using the AND connector. + +Built-in validators +------------------- + +Django Menu Generator has the following built-in validators: + +- is_superuser: + + A validator to check if the authenticated user is a superuser + + Usage: + + .. code:: python + + "validators": ['menu_generator.validators.is_superuser'] + +- is_staff: + + A validator to check if the authenticated user is member of the staff + + Usage: + + .. code:: python + + "validators": ['menu_generator.validators.is_staff'] + +- is_authenticated: + + A validator to check if user is authenticated + + Usage: + + .. code:: python + + "validators": ['menu_generator.validators.is_authenticated'] + +- is_anonymous: + + A validator to check if the user is not authenticated + + Usage: + + .. code:: python + + "validators": ['menu_generator.validators.is_anonymous'] + +- user_has_permission: + + A validator to check if the user has the given permission + + Usage: + + .. code:: python + + "validators": [ + ('menu_generator.validators.user_has_permission', 'app_label.permission_codename') + ] + +- More than one validator: + + You can pass more than one validator to evaluate using the AND connector + + .. code:: python + + "validators": [ + 'menu_generator.validators.is_staff', + ('menu_generator.validators.user_has_permission', 'some_app.some_permission') + ... + ] + +Custom validators +----------------- + +You can build your own validators and use them with Django Menu Generator + +Let's build a validator that checks if the user have more than one pet (dummy example) assuming the user has a +many to many relation called pets + +Assuming we build the function inside ``your_project/app1`` on a ``menu_validators.py`` we have: + +.. code:: python + + # Remember you always must to past the request as first parameter + def has_more_than_one_pet(request): + + return request.user.pets.count() > 0 + +So we can use it as a validator + +.. code:: python + + "validators": ['your_project.app1.menu_validators.has_more_than_one_pet'] + +Now let's build a validator that checks if the user's pet belongs to a specific type to illustrate the validators with +parameters. + +Assuming we build the function inside the same path and the user have a foreign key called pet + +.. code:: python + + def has_a_pet_of_type(request, type): + + return request.user.pet.type == type + +So we use the validator like this: + +.. code:: python + + "validators": [ + ('your_project.app1.menu_validators.has_a_pet_of_type', 'DOG') + ] + +As you can see, we use tuples to pass parameters to the validators, where the first position is the validator and the rest are +the function parameters \ No newline at end of file diff --git a/docs/_build_html/changelog.html b/docs/_build_html/changelog.html index dce67b0acff1f0c7871b3d1edf5e23e4f10aa01b..bde3381a637f789d3e876188dfb9f25b20425598 100644 --- a/docs/_build_html/changelog.html +++ b/docs/_build_html/changelog.html @@ -45,6 +45,22 @@ <div class="section" id="changelog"> <h1>CHANGELOG<a class="headerlink" href="#changelog" title="Permalink to this headline">¶</a></h1> +<div class="section" id="id1"> +<h2>1.0.1(2017-04-29)<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h2> +<ul class="simple"> +<li>Added docs</li> +<li>Readme enhanced</li> +<li>BUGFIX: Added a correction to the validators evaluation with the AND connector</li> +<li>Added flake8 to CI</li> +<li>Added tox</li> +</ul> +</div> +<div class="section" id="id2"> +<h2>1.0.0 (2017-04-09)<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h2> +<ul class="simple"> +<li>Initial release</li> +</ul> +</div> </div> @@ -52,7 +68,16 @@ </div> </div> <div class="sphinxsidebar" role="navigation" aria-label="main navigation"> - <div class="sphinxsidebarwrapper"><div class="relations"> + <div class="sphinxsidebarwrapper"> + <h3><a href="index.html">Table Of Contents</a></h3> + <ul> +<li><a class="reference internal" href="#">CHANGELOG</a><ul> +<li><a class="reference internal" href="#id1">1.0.1(2017-04-29)</a></li> +<li><a class="reference internal" href="#id2">1.0.0 (2017-04-09)</a></li> +</ul> +</li> +</ul> +<div class="relations"> <h3>Related Topics</h3> <ul> <li><a href="index.html">Documentation overview</a><ul> diff --git a/docs/_build_html/index.html b/docs/_build_html/index.html index 3561654c55301ef28c91c4b9fddeba7e12869891..38399ce69771f634a4db36ce213c0aa6f61ff5a5 100644 --- a/docs/_build_html/index.html +++ b/docs/_build_html/index.html @@ -66,10 +66,27 @@ Authentications or whatever you want)</li> <ul> <li class="toctree-l1"><a class="reference internal" href="install.html">Installation</a></li> <li class="toctree-l1"><a class="reference internal" href="usage.html">Usage</a></li> -<li class="toctree-l1"><a class="reference internal" href="menugeneration.html">Menu Generation</a></li> -<li class="toctree-l1"><a class="reference internal" href="urls.html">URLs</a></li> -<li class="toctree-l1"><a class="reference internal" href="validators.html">Validators</a></li> -<li class="toctree-l1"><a class="reference internal" href="changelog.html">CHANGELOG</a></li> +<li class="toctree-l1"><a class="reference internal" href="menugeneration.html">Menu Generation</a><ul> +<li class="toctree-l2"><a class="reference internal" href="menugeneration.html#generating-menus-through-settings">Generating menus through settings</a></li> +<li class="toctree-l2"><a class="reference internal" href="menugeneration.html#generating-menus-through-apps">Generating menus through apps</a></li> +</ul> +</li> +<li class="toctree-l1"><a class="reference internal" href="urls.html">URLs</a><ul> +<li class="toctree-l2"><a class="reference internal" href="urls.html#raw-urls">Raw URLs</a></li> +<li class="toctree-l2"><a class="reference internal" href="urls.html#reversible-urls">Reversible URLs</a></li> +<li class="toctree-l2"><a class="reference internal" href="urls.html#url-with-args-or-kwargs">URL with args or kwargs</a></li> +</ul> +</li> +<li class="toctree-l1"><a class="reference internal" href="validators.html">Validators</a><ul> +<li class="toctree-l2"><a class="reference internal" href="validators.html#built-in-validators">Built-in validators</a></li> +<li class="toctree-l2"><a class="reference internal" href="validators.html#custom-validators">Custom validators</a></li> +</ul> +</li> +<li class="toctree-l1"><a class="reference internal" href="changelog.html">CHANGELOG</a><ul> +<li class="toctree-l2"><a class="reference internal" href="changelog.html#id1">1.0.1(2017-04-29)</a></li> +<li class="toctree-l2"><a class="reference internal" href="changelog.html#id2">1.0.0 (2017-04-09)</a></li> +</ul> +</li> <li class="toctree-l1"><a class="reference internal" href="tests.html">Running the tests</a></li> <li class="toctree-l1"><a class="reference internal" href="contribute.html">Contribute</a></li> <li class="toctree-l1"><a class="reference internal" href="support.html">Support</a></li> diff --git a/docs/_build_html/menugeneration.html b/docs/_build_html/menugeneration.html index 6fba2ee723b81a2aca0f9065e08fb55e8e833292..431e34f7475ae2468e3fcbfa2ea361de471f9a16 100644 --- a/docs/_build_html/menugeneration.html +++ b/docs/_build_html/menugeneration.html @@ -26,7 +26,7 @@ <script type="text/javascript" src="_static/doctools.js"></script> <link rel="index" title="Index" href="genindex.html" /> <link rel="search" title="Search" href="search.html" /> - <link rel="next" title="<no title>" href="urls.html" /> + <link rel="next" title="URLs" href="urls.html" /> <link rel="prev" title="Usage" href="usage.html" /> <link rel="stylesheet" href="_static/custom.css" type="text/css" /> @@ -45,6 +45,77 @@ <div class="section" id="menu-generation"> <h1>Menu Generation<a class="headerlink" href="#menu-generation" title="Permalink to this headline">¶</a></h1> +<p>Django Menu Generator uses python dictionaries to represent the menu items, usually a menu item is as follows:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="p">{</span> + <span class="s2">"name"</span><span class="p">:</span> <span class="s1">'some name'</span><span class="p">,</span> + <span class="s2">"icon_class"</span><span class="p">:</span> <span class="s1">'some icon class'</span><span class="p">,</span> + <span class="s2">"url"</span><span class="p">:</span> <span class="n">URL</span> <span class="n">spec</span><span class="p">,</span> + <span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span> <span class="nb">list</span> <span class="n">of</span> <span class="n">validators</span> <span class="p">],</span> + <span class="s2">"submenu"</span><span class="p">:</span> <span class="n">Dictionary</span> <span class="n">like</span> <span class="n">this</span> +<span class="p">}</span> +</pre></div> +</div> +<p>Where each key is as follows:</p> +<ul class="simple"> +<li><code class="docutils literal"><span class="pre">name</span></code>: A string representing the label of the menu item. If you are using i18n here you can pass the name with the <code class="docutils literal"><span class="pre">ugettext_lazy</span></code> function</li> +<li><code class="docutils literal"><span class="pre">icon_class</span></code>: A string representing the class of the icon you wish to show on the menu item, e.g you can use font-awesome</li> +<li><code class="docutils literal"><span class="pre">url</span></code>: See <a class="reference internal" href="urls.html"><span class="doc">URLs</span></a></li> +<li><code class="docutils literal"><span class="pre">validators</span></code>: See <a class="reference internal" href="validators.html"><span class="doc">Validators</span></a></li> +<li><code class="docutils literal"><span class="pre">submenu</span></code>: You can create infinite nested submenus passing here menu items like this</li> +</ul> +<p>Django Menu Generator offers two ways to generate the menus, through the Django settings and through each of the Django +apps</p> +<div class="section" id="generating-menus-through-settings"> +<h2>Generating menus through settings<a class="headerlink" href="#generating-menus-through-settings" title="Permalink to this headline">¶</a></h2> +<p>You can add various list dictionaries representing each menu you have as explained in <a class="reference internal" href="usage.html"><span class="doc">Usage</span></a> +We recommend to have a <code class="docutils literal"><span class="pre">menus.py</span></code> file with the menu list dictionaries and then import it to the settings file if you +go this way</p> +</div> +<div class="section" id="generating-menus-through-apps"> +<h2>Generating menus through apps<a class="headerlink" href="#generating-menus-through-apps" title="Permalink to this headline">¶</a></h2> +<p>Some people prefer to isolate all the aspects of a project between apps, so, we add this feature to allow the menus +live inside each app.</p> +<p>You need to add inside the app a <code class="docutils literal"><span class="pre">menus.py</span></code> file that contains a dictionary called <code class="docutils literal"><span class="pre">MENUS</span></code>, each element of the +dictionary will be a menu list dictionary with all the configuration needed to display that menu, e.g:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="n">MENUS</span> <span class="o">=</span> <span class="p">{</span> + <span class="s1">'NAV_MENU_LEFT'</span><span class="p">:</span> <span class="p">[</span> + <span class="p">{</span> + <span class="s2">"name"</span><span class="p">:</span> <span class="s2">"App1 Feature"</span><span class="p">,</span> + <span class="s2">"url"</span><span class="p">:</span> <span class="s2">"/app1-feature"</span> + <span class="p">}</span> + <span class="p">],</span> + <span class="s1">'NAV_MENU_TOP'</span><span class="p">:</span> <span class="p">[</span> + <span class="p">{</span> + <span class="s2">"name"</span><span class="p">:</span> <span class="s2">"Second Menu Feature"</span><span class="p">,</span> + <span class="s2">"url"</span><span class="p">:</span> <span class="s2">"named_url"</span> + <span class="p">}</span> + <span class="p">]</span> +<span class="p">}</span> +</pre></div> +</div> +<p>So, as an example, for the <code class="docutils literal"><span class="pre">'NAV_MENU_LEFT'</span></code>, Django Menu Generator will loop each app searching for the <code class="docutils literal"><span class="pre">'NAV_MENU_LEFT'</span></code> +list dictionaries inside of the <code class="docutils literal"><span class="pre">MENUS</span></code> and build all the menu configuration to build the whole menu.</p> +<p>With this feature you can have a project structure like this:</p> +<div class="highlight-default"><div class="highlight"><pre><span></span>your_project/ +├── config_folder/ +│ └── ... +├── app1 +│ └── models.py +│ forms.py +│ views.py +│ menus.py +│ +├── app2 +│ └── models.py +│ forms.py +│ views.py +│ menus.py +│ + ... +</pre></div> +</div> +<p>You can have a mix of the two approaches if you wish</p> +</div> </div> @@ -52,12 +123,21 @@ </div> </div> <div class="sphinxsidebar" role="navigation" aria-label="main navigation"> - <div class="sphinxsidebarwrapper"><div class="relations"> + <div class="sphinxsidebarwrapper"> + <h3><a href="index.html">Table Of Contents</a></h3> + <ul> +<li><a class="reference internal" href="#">Menu Generation</a><ul> +<li><a class="reference internal" href="#generating-menus-through-settings">Generating menus through settings</a></li> +<li><a class="reference internal" href="#generating-menus-through-apps">Generating menus through apps</a></li> +</ul> +</li> +</ul> +<div class="relations"> <h3>Related Topics</h3> <ul> <li><a href="index.html">Documentation overview</a><ul> <li>Previous: <a href="usage.html" title="previous chapter">Usage</a></li> - <li>Next: <a href="urls.html" title="next chapter"><no title></a></li> + <li>Next: <a href="urls.html" title="next chapter">URLs</a></li> </ul></li> </ul> </div> diff --git a/docs/_build_html/searchindex.js b/docs/_build_html/searchindex.js index c8be8b7f205735eb6b2f14f43b1521921e41f85f..4fa264ae715d4bae78e6146a92cdb57dda445259 100644 --- a/docs/_build_html/searchindex.js +++ b/docs/_build_html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["changelog","contribute","credits","index","install","license","menugeneration","support","tests","urls","usage","validators"],envversion:51,filenames:["changelog.rst","contribute.rst","credits.rst","index.rst","install.rst","license.rst","menugeneration.rst","support.rst","tests.rst","urls.rst","usage.rst","validators.rst"],objects:{},objnames:{},objtypes:{},terms:{"class":10,"float":10,Then:[],_gener:[],about:10,abov:[],account:10,acount:10,activ:10,add:10,address:10,against:8,app:10,authent:3,author:2,automat:3,balanc:10,bar:10,bodi:10,breadcrum:3,build:[3,10],can:[4,10],changelog:3,clone:4,code:1,com:[1,2,4,7,10],condit:3,contact:[7,10],contain:10,contribut:3,credit:3,current:8,databas:3,dictionari:[3,10],directori:4,displai:3,div:10,django:[1,2,4,10],doc:[],doctyp:10,download:4,each:3,easy_instal:4,enabl:3,end:10,endfor:10,endif:10,environ:8,exampl:10,facebook:10,file:[4,10],foobar:10,footer:10,footer_menu_left:10,footer_menu_right:10,full:3,gener:[1,4,10],get_menu:10,git:4,github:[1,2,4],handl:10,have:7,head:10,his:3,home:10,href:10,html:[3,10],http:[1,2,4],icon:3,icon_class:10,identifi:3,insid:10,instal:[3,10],installed_app:10,instruct:[],is_anonym:10,is_authent:10,is_paid_us:10,is_superus:10,issu:[1,7],item:[3,10],kneeman:2,know:7,left:10,left_footer_menu:10,left_menu:10,let:7,licens:3,like:[2,3],list:[3,10],load:10,login:10,login_url_view:10,loop:10,manag:8,mantain:7,master:4,menu:[1,4,10],menu_gener:10,menuwar:2,mit:5,more:10,must:10,myapp:10,name:[2,10],nav:10,nav_menu_left:10,nav_menu_right:10,need:3,now:10,onc:[3,10],one:4,onli:3,opensourc:7,option:4,origin:2,per:[],permiss:3,pip:4,pleas:7,product:3,profil:10,project:[2,7,10],python:[3,4,8],radyconsultor:[1,4,7],regist:10,register_view_url:10,releas:5,revers:10,right:10,right_footer_menu:10,right_menu:10,run:[3,4,10],secret:10,see:10,select:[3,10],semi:3,set:10,setup:[3,4],sourc:1,start:10,structur:3,style:10,submenu:10,support:3,tag:10,templat:10,test:3,thank:2,thi:[2,7],through:[3,10],titl:10,tool:3,tracker:1,un33k:2,under:[2,5],unlimit:3,unzip:4,url:[3,10],usag:3,use:3,val:2,valid:[3,10],want:3,wget:4,whatev:3,when:10,would:2,you:[3,4,7,10],your:10,zipbal:4},titles:["CHANGELOG","Contribute","Credits","Welcome to Django Menu Generator’s documentation!","Installation","License","Menu Generation","Support","Running the tests","URLs","Usage","Validators"],titleterms:{changelog:0,content:3,contribut:1,credit:2,django:3,document:3,featur:3,gener:[3,6],instal:4,licens:5,menu:[3,6],run:8,support:7,test:8,url:9,usag:10,valid:11,welcom:3}}) \ No newline at end of file +Search.setIndex({docnames:["changelog","contribute","credits","index","install","license","menugeneration","support","tests","urls","usage","validators"],envversion:51,filenames:["changelog.rst","contribute.rst","credits.rst","index.rst","install.rst","license.rst","menugeneration.rst","support.rst","tests.rst","urls.rst","usage.rst","validators.rst"],objects:{},objnames:{},objtypes:{},terms:{"boolean":11,"class":[6,10],"float":10,"function":[6,11],"import":6,"return":11,AND:[0,11],Added:0,For:9,Then:[],With:6,_gener:[],about:10,abov:[],access:[],account:10,acount:10,activ:10,add:[6,10],address:10,against:8,all:[6,11],allow:[6,11],alwai:11,ani:9,app1:[6,11],app2:6,app:[3,10],app_label:11,approach:6,arg:[3,11],as_view:9,aspect:6,assum:11,authent:[3,11],author:2,automat:3,awesom:6,balanc:10,bar:10,belong:11,between:6,bodi:10,breadcrum:3,bugfix:0,build:[3,6,10,11],built:3,call:[6,11],callabl:11,can:[4,6,9,10,11],changelog:3,check:11,clone:4,code:[1,9],com:[1,2,4,7,10],condit:3,config_fold:6,configur:6,connector:[0,11],construct:[],contact:[7,10],contain:[6,10,11],contribut:3,cookiecutt:[],correct:0,count:11,creat:6,credit:3,current:8,current_app:9,custom:3,databas:3,def:11,dictionari:[3,6,9,10],directori:4,displai:[3,6,11],div:10,django:[1,2,4,6,9,10,11],doc:0,doctyp:10,dog:11,download:4,dummi:11,each:[3,6],easy_instal:4,element:6,enabl:3,end:10,endfor:10,endif:10,enhanc:0,environ:8,evalu:[0,11],exampl:[6,10,11],explain:6,facebook:10,fact:9,featur:[6,9],file:[4,6,10],first:11,flake8:0,follow:[6,9,11],font:6,foobar:10,footer:10,footer_menu_left:10,footer_menu_right:10,foreign:11,form:6,full:3,gener:[1,4,10,11],get_menu:10,git:4,github:[1,2,4],given:11,handl:10,hard:9,has:11,has_a_pet_of_typ:11,has_more_than_one_pet:11,have:[6,7,9,11],head:10,here:6,his:3,home:10,href:10,html:[3,10],http:[1,2,4],i18n:6,icon:[3,6],icon_class:[6,10],identifi:3,illustr:11,indic:11,infinit:6,initi:0,insid:[6,10,11],instal:[3,10],installed_app:10,instruct:[],is_anonym:[10,11],is_authent:[10,11],is_paid_us:10,is_staff:11,is_superus:[10,11],isol:6,issu:[1,7],item:[3,6,9,10,11],json:[],kei:[6,11],kneeman:2,know:7,kwarg:3,label:6,least:11,left:10,left_footer_menu:10,left_menu:10,let:[7,11],licens:3,like:[2,3,6,9,11],list:[3,6,10,11],live:6,load:10,login:10,login_url_view:10,loop:[6,10],manag:8,mani:11,mantain:7,master:4,member:11,menu:[1,4,9,10,11],menu_gener:[10,11],menu_valid:11,menuwar:2,method:9,mit:5,mix:6,model:6,more:[10,11],must:[10,11],myapp:10,name:[2,6,9,10],named_url:[6,9],nav:10,nav_menu_left:[6,10],nav_menu_right:10,nav_menu_top:6,need:[3,6],nest:6,none:9,normal:[],now:[10,11],offer:6,onc:[3,10],one:[4,11],onli:3,opensourc:7,option:4,origin:2,own:11,paramet:[9,11],pass:[6,9,11],past:11,path:[9,11],peopl:6,per:[],permiss:[3,11],permission_codenam:11,pet:11,pip:4,pleas:7,posit:11,prefer:6,product:3,profil:10,project:[2,6,7,10],python:[3,4,6,8,11],radyconsultor:[1,4,7],raw:3,readm:0,receiv:11,recommend:6,regist:10,register_view_url:10,relat:11,releas:[0,5],rememb:11,repres:6,request:11,rest:11,revers:[3,10],right:10,right_footer_menu:10,right_menu:10,run:[3,4,10],same:11,search:6,second:6,secret:10,see:[6,10,11],select:[3,10],semi:3,set:[3,10],setup:[3,4],show:6,sign:9,some:[6,9],some_app:11,some_permiss:11,someview:9,sourc:1,spec:6,specif:11,staff:11,start:10,string:6,structur:[3,6],style:10,submenu:[6,10],superus:11,support:3,tag:10,templat:10,test:3,than:11,thank:2,them:11,thi:[2,6,7,9,11],three:9,through:[3,9,10],titl:10,tool:3,tox:0,tracker:1,tupl:11,two:6,type:11,ugettext_lazi:6,un33k:2,under:[2,5],unlimit:3,unzip:4,updat:9,url:[3,6,10],urlconf:9,usag:[3,6,11],use:[3,6,11],user:11,user_has_permiss:11,uses:[6,11],using:[6,11],usual:6,val:2,valid:[0,3,6,10],variabl:[],variou:6,view:6,viewnam:9,wai:[6,9],want:3,wget:4,whatev:3,when:10,where:[6,11],whole:6,wish:6,would:2,you:[3,4,6,7,9,10,11],your:[10,11],your_project:[6,11],zipbal:4},titles:["CHANGELOG","Contribute","Credits","Welcome to Django Menu Generator’s documentation!","Installation","License","Menu Generation","Support","Running the tests","URLs","Usage","Validators"],titleterms:{app:6,arg:9,built:11,changelog:0,content:3,contribut:1,credit:2,custom:11,django:3,document:3,featur:3,gener:[3,6],instal:4,kwarg:9,licens:5,menu:[3,6],raw:9,revers:9,run:8,set:6,support:7,test:8,through:6,url:9,usag:10,valid:11,welcom:3}}) \ No newline at end of file diff --git a/docs/_build_html/urls.html b/docs/_build_html/urls.html index 8dc8ddfb1f4cb357200ff3fb6ed54db1eb60b966..ba859d0405660bc5116e5ed698ca9866ee88ea62 100644 --- a/docs/_build_html/urls.html +++ b/docs/_build_html/urls.html @@ -45,6 +45,33 @@ <div class="section" id="urls"> <h1>URLs<a class="headerlink" href="#urls" title="Permalink to this headline">¶</a></h1> +<p>You can pass the URL parameters to menu items in three ways.</p> +<div class="section" id="raw-urls"> +<h2>Raw URLs<a class="headerlink" href="#raw-urls" title="Permalink to this headline">¶</a></h2> +<p>A hard-coded url:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"url"</span><span class="p">:</span> <span class="s1">'/some-path/to-feature'</span> +</pre></div> +</div> +</div> +<div class="section" id="reversible-urls"> +<h2>Reversible URLs<a class="headerlink" href="#reversible-urls" title="Permalink to this headline">¶</a></h2> +<p>An url that can be reversed with the <cite>reverse</cite> method:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"url"</span><span class="p">:</span> <span class="s1">'named_url'</span> +</pre></div> +</div> +</div> +<div class="section" id="url-with-args-or-kwargs"> +<h2>URL with args or kwargs<a class="headerlink" href="#url-with-args-or-kwargs" title="Permalink to this headline">¶</a></h2> +<p>e.g. If you have an url with kwargs like this:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="n">url</span><span class="p">(</span><span class="sa">r</span><span class="s1">'^update/(?P<pk>\d+)/$'</span><span class="p">,</span> <span class="n">SomeView</span><span class="o">.</span><span class="n">as_view</span><span class="p">(),</span> <span class="n">name</span><span class="o">=</span><span class="s1">'update'</span><span class="p">),</span> +</pre></div> +</div> +<p>you can pass the url as follows:</p> +<blockquote> +<div>“url”: {“viewname”: ‘update’, “kwargs”: {“pk”: 1}}</div></blockquote> +<p>In fact, you can pass any of the parameters of the reverse method through the dictionary</p> +<p>For Django 1.10 the reverse method sign is: <code class="docutils literal"><span class="pre">reverse(viewname,</span> <span class="pre">urlconf=None,</span> <span class="pre">args=None,</span> <span class="pre">kwargs=None,</span> <span class="pre">current_app=None)</span></code></p> +</div> </div> @@ -52,7 +79,17 @@ </div> </div> <div class="sphinxsidebar" role="navigation" aria-label="main navigation"> - <div class="sphinxsidebarwrapper"><div class="relations"> + <div class="sphinxsidebarwrapper"> + <h3><a href="index.html">Table Of Contents</a></h3> + <ul> +<li><a class="reference internal" href="#">URLs</a><ul> +<li><a class="reference internal" href="#raw-urls">Raw URLs</a></li> +<li><a class="reference internal" href="#reversible-urls">Reversible URLs</a></li> +<li><a class="reference internal" href="#url-with-args-or-kwargs">URL with args or kwargs</a></li> +</ul> +</li> +</ul> +<div class="relations"> <h3>Related Topics</h3> <ul> <li><a href="index.html">Documentation overview</a><ul> diff --git a/docs/_build_html/validators.html b/docs/_build_html/validators.html index c0ac7cc0d0e212b5e78b1afc0046e6b3a47bb242..90447716e1a8fefdbddb154fc200c7e0545e8a33 100644 --- a/docs/_build_html/validators.html +++ b/docs/_build_html/validators.html @@ -45,6 +45,108 @@ <div class="section" id="validators"> <h1>Validators<a class="headerlink" href="#validators" title="Permalink to this headline">¶</a></h1> +<p>Django Menu Generator uses validators to allow the displaying of menu items.</p> +<p>A validator is a function that receives the request as arg and returns a boolean indicating if the check has passed</p> +<p>for Django Menu Generator the validators must always be a list containing at least one callable or python path to a callable. +If there is more than one validator, all of them will be evaluated using the AND connector.</p> +<div class="section" id="built-in-validators"> +<h2>Built-in validators<a class="headerlink" href="#built-in-validators" title="Permalink to this headline">¶</a></h2> +<p>Django Menu Generator has the following built-in validators:</p> +<ul> +<li><p class="first">is_superuser:</p> +<blockquote> +<div><p>A validator to check if the authenticated user is a superuser</p> +<p>Usage:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span><span class="s1">'menu_generator.validators.is_superuser'</span><span class="p">]</span> +</pre></div> +</div> +</div></blockquote> +</li> +<li><p class="first">is_staff:</p> +<blockquote> +<div><p>A validator to check if the authenticated user is member of the staff</p> +<p>Usage:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span><span class="s1">'menu_generator.validators.is_staff'</span><span class="p">]</span> +</pre></div> +</div> +</div></blockquote> +</li> +<li><p class="first">is_authenticated:</p> +<blockquote> +<div><p>A validator to check if user is authenticated</p> +<p>Usage:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span><span class="s1">'menu_generator.validators.is_authenticated'</span><span class="p">]</span> +</pre></div> +</div> +</div></blockquote> +</li> +<li><p class="first">is_anonymous:</p> +<blockquote> +<div><p>A validator to check if the user is not authenticated</p> +<p>Usage:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span><span class="s1">'menu_generator.validators.is_anonymous'</span><span class="p">]</span> +</pre></div> +</div> +</div></blockquote> +</li> +<li><p class="first">user_has_permission:</p> +<blockquote> +<div><p>A validator to check if the user has the given permission</p> +<p>Usage:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span> + <span class="p">(</span><span class="s1">'menu_generator.validators.user_has_permission'</span><span class="p">,</span> <span class="s1">'app_label.permission_codename'</span><span class="p">)</span> +<span class="p">]</span> +</pre></div> +</div> +</div></blockquote> +</li> +<li><p class="first">More than one validator:</p> +<blockquote> +<div><p>You can pass more than one validator to evaluate using the AND connector</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span> + <span class="s1">'menu_generator.validators.is_staff'</span><span class="p">,</span> + <span class="p">(</span><span class="s1">'menu_generator.validators.user_has_permission'</span><span class="p">,</span> <span class="s1">'some_app.some_permission'</span><span class="p">)</span> + <span class="o">...</span> +<span class="p">]</span> +</pre></div> +</div> +</div></blockquote> +</li> +</ul> +</div> +<div class="section" id="custom-validators"> +<h2>Custom validators<a class="headerlink" href="#custom-validators" title="Permalink to this headline">¶</a></h2> +<p>You can build your own validators and use them with Django Menu Generator</p> +<p>Let’s build a validator that checks if the user have more than one pet (dummy example) assuming the user has a +many to many relation called pets</p> +<p>Assuming we build the function inside <code class="docutils literal"><span class="pre">your_project/app1</span></code> on a <code class="docutils literal"><span class="pre">menu_validators.py</span></code> we have:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="c1"># Remember you always must to past the request as first parameter</span> +<span class="k">def</span> <span class="nf">has_more_than_one_pet</span><span class="p">(</span><span class="n">request</span><span class="p">):</span> + + <span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">pets</span><span class="o">.</span><span class="n">count</span><span class="p">()</span> <span class="o">></span> <span class="mi">0</span> +</pre></div> +</div> +<p>So we can use it as a validator</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span><span class="s1">'your_project.app1.menu_validators.has_more_than_one_pet'</span><span class="p">]</span> +</pre></div> +</div> +<p>Now let’s build a validator that checks if the user’s pet belongs to a specific type to illustrate the validators with +parameters.</p> +<p>Assuming we build the function inside the same path and the user have a foreign key called pet</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">has_a_pet_of_type</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="nb">type</span><span class="p">):</span> + + <span class="k">return</span> <span class="n">request</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">pet</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="nb">type</span> +</pre></div> +</div> +<p>So we use the validator like this:</p> +<div class="code python highlight-default"><div class="highlight"><pre><span></span><span class="s2">"validators"</span><span class="p">:</span> <span class="p">[</span> + <span class="p">(</span><span class="s1">'your_project.app1.menu_validators.has_a_pet_of_type'</span><span class="p">,</span> <span class="s1">'DOG'</span><span class="p">)</span> +<span class="p">]</span> +</pre></div> +</div> +<p>As you can see, we use tuples to pass parameters to the validators, where the first position is the validator and the rest are +the function parameters</p> +</div> </div> @@ -52,7 +154,16 @@ </div> </div> <div class="sphinxsidebar" role="navigation" aria-label="main navigation"> - <div class="sphinxsidebarwrapper"><div class="relations"> + <div class="sphinxsidebarwrapper"> + <h3><a href="index.html">Table Of Contents</a></h3> + <ul> +<li><a class="reference internal" href="#">Validators</a><ul> +<li><a class="reference internal" href="#built-in-validators">Built-in validators</a></li> +<li><a class="reference internal" href="#custom-validators">Custom validators</a></li> +</ul> +</li> +</ul> +<div class="relations"> <h3>Related Topics</h3> <ul> <li><a href="index.html">Documentation overview</a><ul> diff --git a/docs/changelog.rst b/docs/changelog.rst index c4df4750f73b208f7e0981d82a71606e2bed26e4..33a4f832eef17824a9098535a13c11c3aa69586d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,2 +1,16 @@ CHANGELOG ========= + +1.0.1(2017-04-29) +----------------- + +- Added docs +- Readme enhanced +- BUGFIX: Added a correction to the validators evaluation with the AND connector +- Added flake8 to CI +- Added tox + +1.0.0 (2017-04-09) +------------------ + +- Initial release \ No newline at end of file diff --git a/docs/menugeneration.rst b/docs/menugeneration.rst index f1426f797bbbfe777838599534b5295b38f74420..23e2846d55320072304b383f27fcac74dd6ef515 100644 --- a/docs/menugeneration.rst +++ b/docs/menugeneration.rst @@ -1,2 +1,86 @@ Menu Generation =============== + +Django Menu Generator uses python dictionaries to represent the menu items, usually a menu item is as follows: + +.. code:: python + + { + "name": 'some name', + "icon_class": 'some icon class', + "url": URL spec, + "validators": [ list of validators ], + "submenu": Dictionary like this + } + +Where each key is as follows: + +- ``name``: A string representing the label of the menu item. If you are using i18n here you can pass the name with the ``ugettext_lazy`` function + +- ``icon_class``: A string representing the class of the icon you wish to show on the menu item, e.g you can use font-awesome + +- ``url``: See :doc:`urls` + +- ``validators``: See :doc:`validators` + +- ``submenu``: You can create infinite nested submenus passing here menu items like this + +Django Menu Generator offers two ways to generate the menus, through the Django settings and through each of the Django +apps + +Generating menus through settings +--------------------------------- + +You can add various list dictionaries representing each menu you have as explained in :doc:`usage` +We recommend to have a ``menus.py`` file with the menu list dictionaries and then import it to the settings file if you +go this way + +Generating menus through apps +----------------------------- + +Some people prefer to isolate all the aspects of a project between apps, so, we add this feature to allow the menus +live inside each app. + +You need to add inside the app a ``menus.py`` file that contains a dictionary called ``MENUS``, each element of the +dictionary will be a menu list dictionary with all the configuration needed to display that menu, e.g: + +.. code:: python + + MENUS = { + 'NAV_MENU_LEFT': [ + { + "name": "App1 Feature", + "url": "/app1-feature" + } + ], + 'NAV_MENU_TOP': [ + { + "name": "Second Menu Feature", + "url": "named_url" + } + ] + } + +So, as an example, for the ``'NAV_MENU_LEFT'``, Django Menu Generator will loop each app searching for the ``'NAV_MENU_LEFT'`` +list dictionaries inside of the ``MENUS`` and build all the menu configuration to build the whole menu. + +With this feature you can have a project structure like this:: + + your_project/ + ├── config_folder/ + │ └── ... + ├── app1 + │ └── models.py + │ forms.py + │ views.py + │ menus.py + │ + ├── app2 + │ └── models.py + │ forms.py + │ views.py + │ menus.py + │ + ... + +You can have a mix of the two approaches if you wish \ No newline at end of file diff --git a/docs/urls.rst b/docs/urls.rst index b5d18e70783d30cbc8de7728036d130af5452890..c7e4e8d43aaa53e262a4c525e232c87cf8af8406 100644 --- a/docs/urls.rst +++ b/docs/urls.rst @@ -1,2 +1,40 @@ URLs ==== + +You can pass the URL parameters to menu items in three ways. + +Raw URLs +-------- + +A hard-coded url: + +.. code:: python + + "url": '/some-path/to-feature' + +Reversible URLs +--------------- + +An url that can be reversed with the `reverse` method: + +.. code:: python + + "url": 'named_url' + +URL with args or kwargs +----------------------- + +e.g. If you have an url with kwargs like this: + +.. code:: python + + url(r'^update/(?P<pk>\d+)/$', SomeView.as_view(), name='update'), + +you can pass the url as follows: + + "url": {"viewname": 'update', "kwargs": {"pk": 1}} + +In fact, you can pass any of the parameters of the reverse method through the dictionary + +For Django 1.10 the reverse method sign is: ``reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)`` + diff --git a/docs/validators.rst b/docs/validators.rst index 70fbe5759b5eb4d64d8203f7bb1a83d2057deeaf..839126a9e2eee5b6da9eba713cb2ba6ae1238cf8 100644 --- a/docs/validators.rst +++ b/docs/validators.rst @@ -1,2 +1,123 @@ Validators ========== + +Django Menu Generator uses validators to allow the displaying of menu items. + +A validator is a function that receives the request as arg and returns a boolean indicating if the check has passed + +for Django Menu Generator the validators must always be a list containing at least one callable or python path to a callable. +If there is more than one validator, all of them will be evaluated using the AND connector. + +Built-in validators +------------------- + +Django Menu Generator has the following built-in validators: + +- is_superuser: + + A validator to check if the authenticated user is a superuser + + Usage: + + .. code:: python + + "validators": ['menu_generator.validators.is_superuser'] + +- is_staff: + + A validator to check if the authenticated user is member of the staff + + Usage: + + .. code:: python + + "validators": ['menu_generator.validators.is_staff'] + +- is_authenticated: + + A validator to check if user is authenticated + + Usage: + + .. code:: python + + "validators": ['menu_generator.validators.is_authenticated'] + +- is_anonymous: + + A validator to check if the user is not authenticated + + Usage: + + .. code:: python + + "validators": ['menu_generator.validators.is_anonymous'] + +- user_has_permission: + + A validator to check if the user has the given permission + + Usage: + + .. code:: python + + "validators": [ + ('menu_generator.validators.user_has_permission', 'app_label.permission_codename') + ] + +- More than one validator: + + You can pass more than one validator to evaluate using the AND connector + + .. code:: python + + "validators": [ + 'menu_generator.validators.is_staff', + ('menu_generator.validators.user_has_permission', 'some_app.some_permission') + ... + ] + +Custom validators +----------------- + +You can build your own validators and use them with Django Menu Generator + +Let's build a validator that checks if the user have more than one pet (dummy example) assuming the user has a +many to many relation called pets + +Assuming we build the function inside ``your_project/app1`` on a ``menu_validators.py`` we have: + +.. code:: python + + # Remember you always must to past the request as first parameter + def has_more_than_one_pet(request): + + return request.user.pets.count() > 0 + +So we can use it as a validator + +.. code:: python + + "validators": ['your_project.app1.menu_validators.has_more_than_one_pet'] + +Now let's build a validator that checks if the user's pet belongs to a specific type to illustrate the validators with +parameters. + +Assuming we build the function inside the same path and the user have a foreign key called pet + +.. code:: python + + def has_a_pet_of_type(request, type): + + return request.user.pet.type == type + +So we use the validator like this: + +.. code:: python + + "validators": [ + ('your_project.app1.menu_validators.has_a_pet_of_type', 'DOG') + ] + +As you can see, we use tuples to pass parameters to the validators, where the first position is the validator and the rest are +the function parameters \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 435aa96dafa704236ff2af7a1b5a1e762ccc464f..3867c7acf33a81cdf501f6295971869150164321 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,3 +3,4 @@ description-file = README.md [flake8] max-line-length = 122 +exclude = docs/* \ No newline at end of file