Django – routing layer

1. Route matching

1. Notes on route matching

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # front page
    url(r'^$',views.home),
    # Route matching
    url(r'^test/$',views.test),
    url(r'^testadd/$',views.testadd),
    # Last page (understand): Use exception capture processing later. Such a last page makes Django's second slash APPEND_SLASH=True in the path meaningless, and the mechanism of appending slashes in the second redirect is invalid.
    url(r'',views.error),
]

'''
1. The first parameter of the url method is a regular expression. As long as the first parameter regular expression can match the content, the matching will stop immediately and the corresponding view function will be called directly.
2. There is no need to add a leading backslash since every URL has one. For example, it should be ^test instead of ^/test.
3. The 'r' in front of each regular expression is optional but recommended. r means raw to identify the native string and declare that all characters in the modified string are ordinary characters.
4. When the regular matching mechanism does not match the desired content for the first time, it will add a slash \ after the matched content, and then Django will help you redirect internally and start matching again.
5. Each parameter captured by the regular match is passed to the view as an ordinary Python string.
'''

2. Cancel route matching. If the first match is unsuccessful, add a slash\ after the matching content and redirect to match again

# Cancel the automatic addition of slashes: write the following content in the settings.py file
APPEND_SLASH = False # The default is True to automatically add a slash

2. Group naming matching

1. Unnamed group

# Unnamed group: no need to define a name
    Route writing: url('^index/(\d + )', views.index)
    Internal logic: The content matched by the regular expression in the brackets will be passed as a positional parameter to the view function index bound to it.
    View function:
        def index(request,xx):
            print(xx)
            return HttpResponse('index')

2. Famous groups

# Famous group: need to define a name
    Route writing: url('^index/(?P<year>\d + )', views.index)
    Internal logic: The content matched by the regular expression in the brackets will be passed as a keyword argument to the view function index bound to it.
    View function:
        def index(request,year):
            print(year)
            return HttpResponse('index')

3. Can unknown and famous names be mixed?

# Also known as nameless groups, they cannot be mixed, but they can be used multiple times.
    url('^index/(\d + )/(\d + )/', views.index)
    url('^index/(?P<year>\d + )/(?P<year>\d + )/', views.index)

    
# If used multiple times, if it is an unnamed group, *args can be used in the view function to receive parameters.
def index(request, *args):
    print(args)
    return HttpResponse('index')


# If it is used multiple times, if it is a famous group, you can use **kwargs in the view function to receive parameters.
def index(request, **kwargs):
    print(kwargs)
    return HttpResponse('index')

3. Pass additional parameters to the view function (understand)

URLconfs has a hook that lets you pass a Python dictionary as an extra argument to the view function.

The django.conf.urls.url() function can accept an optional third parameter, which is a dictionary representing additional keyword arguments that you want to pass to the view function.

For example:

from django.conf.urls import url
from .import views

urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

In this example, for the /blog/2005/ request, Django will call views.year_archive(request, year=’2005′, foo=’bar’).
This technique is used in the Syndication framework to pass metadata and options to views.

4. Reverse analysis

Reverse parsing is also called reverse URL matching, reverse URL query or simple URL reverse lookup.

1. What is reverse analysis?

Reverse parsing is to obtain a result through some methods. The result can directly access the corresponding URL to trigger the view function.

2. Basic use of reverse analysis

Step one: Give the routing and view functions an alias in urls.py (note: the alias cannot be repeated and must be unique)
        url('^index/', views.index, name='xxx')
        
Step 2: Use reverse parsing
    Use at the template level:
        {% url 'xxx' %}
        <a href="{% url 'ooo' %}">111</a>
    Use in view layer:
        from django.shortcuts import reverse
        reverse('xxx') 

3. Reverse analysis of unknown and famous groups

3.1 Unnamed group reverse analysis
# Routing layer configures reverse parsing of unnamed packets
    url(r'^index/(\d + )/',views.index,name='xxx')

# The template layer uses reverse parsing
    {% url 'xxx' 123 %}

#The view layer uses reverse parsing
    reverse('xxx', args=(1, ))

"""
The number of routing layer url group matching mechanism is not hard-coded.
Generally, the primary key value of the data is placed. We can locate the data object by obtaining the primary key value of the data.
This allows the data to be edited and deleted, as shown in the following example:
"""
# Routing layer configures reverse parsing of unnamed groups
    url(r'^edit/(\d + )/', views.edit, name='xxx')

#The view layer uses reverse parsing
    def edit(request,edit_id):
        reverse('xxx', args=(edit_id,))

# The template layer uses reverse parsing
    {% for user_obj in user_queryset %}
        <a href="{% url 'xxx' user_obj.id %}">Edit</a>
    {% endfor %}
3.2 Reverse analysis of famous groups
# Reverse parsing of famous groups
   url(r'^func/(?P<year>\d + )/',views.func,name='ooo')

# The template layer uses reverse parsing
    The first way to write:
        <a href="{% url 'ooo' year=123 %}">111</a>
    The second way of writing: recommended
        <a href="{% url 'ooo' 123 %}">222</a>

#The view layer uses reverse parsing
   The first way to write:
        print(reverse('ooo',kwargs={'year':123}))
   The second way of writing:
        print(reverse('ooo',args=(111,)))

4. Route distribution

'''
Each application of Django can have its own templates folder, urls.py file, and static folder. Only based on the above characteristics, Django can achieve group development very well, that is to say, everyone only writes Just use the functions in your own app.
Therefore, as a team leader, you only need to copy all the apps written by you to a new Django project, register all the apps in the configuration file, and then use the routing distribution feature to integrate all the apps.

Therefore, when there are a lot of URLs in a Django project, the overall routing urls.py code is very redundant and difficult to maintain. At this time, route distribution can also be used to reduce the pressure on the overall routing.

After using routing distribution, the overall routing no longer directly corresponds to the routing and view functions, but performs a distribution process to identify the application to which the current URL belongs, and finally distributes it directly to the corresponding application for processing.
'''

General route:

from django.conf.urls import url
from django.conf.urls import include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # method one:
    from app01 import urls as app01_urls
    from app02 import urls as app02_urls
    url(r'^app01/', include(app01_urls)) # As long as the url prefix starts with app01, all will be handled by app01
    url(r'^app02/', include(app02_urls))

    # Method 2: Ultimate writing method recommended
    url(r'^app02/', include('app01.urls'))
    url(r'^app01/', include('app02.urls'))
]
# Note: Do not use the format `^app01/$` in the main route, otherwise it cannot be distributed to sub-routes

Subroute settings:

# Configuration of urls.py in app01
    from django.conf.urls import url
    from app01 import views
    urlpatterns = [
        url(r'^index/', views.index),
    ]

# Configuration of urls.py in app02
    from django.conf.urls import url
    from app02 import views
    urlpatterns = [
        url(r'^index/', views.index),
    ]

5. Namespace

Why do you need namespaces?

"""
When multiple applications set the same alias and use reverse resolution, if the results are the same after matching the routing matching rules,
It will appear that the later one overwrites the previous one. If you want to trigger the previous view function, you will not be able to access it.

Under normal circumstances, reverse parsing cannot automatically identify the prefix.
Note!!!: What is mentioned here is that under normal circumstances, if the number of URL groups corresponding to the reverse analysis is the same, then it can be identified.
"""

Configure namespace in total route:

from django.conf.urls import url
from django.conf.urls import include
urlpatterns = [
    url('^app01/', include('app01.urls', namespace='app01')),
    url('^app02/', include('app02.urls', namespace='app02')),
]

The matching rules in the sub-route are the same as the alias:

# Configuration of urls.py in app01
    from django.conf.urls import url
    from app01 import views
    urlpatterns = [
        url('^reg/', views.reg, name='reg'),
    ]

# Configuration of urls.py in app02
    from django.conf.urls import url
    from app02 import views
    urlpatterns = [
        url('^reg/', views.reg, name='reg'),
    ]

The sub-path uses reverse parsing:

# The view layer uses namespace syntax for reverse parsing
    reverse('app01:reg')
    reverse('app02:reg')
    
# The template layer uses namespace syntax for reverse parsing
    {% url 'app01:reg' %}
    {% url 'app02:reg' %}

Additional: In fact, as long as the names do not conflict, there is no need to use namespaces

"""
Generally, when there are multiple apps, we will add the app prefix when making an alias.
In this way, we can ensure that there is no name conflict between multiple apps.
"""

urlpatterns = [
    url(r'^reg/',views.reg,name='app01_reg')
]
urlpatterns = [
    url(r'^reg/',views.reg,name='app02_reg')
]

6. Pseudo-static

# What is a static web page?
    Static web page data is hard-coded and remains unchanged for thousands of years.

# What is Pseudo-Static state?
    Pseudo-static is to disguise a dynamic web page as a static web page.
    
# Why do we need pseudo-static?
    https://www.cnblogs.com/Dominic-Ji/p/9234099.html
    The purpose of pseudo-static is to increase the SEO query strength of this website and increase the probability of search engines collecting this website.
    (SEO’s full name is Search Engine Optimization)

    In fact, a search engine is essentially a huge crawler program.

    Summarize:
        No matter how you optimize and deal with
        Still can’t defeat RMB players 

The routing layer uses pseudo-static: reg.html

# Routing configuration
urlpatterns = [
    url(r'^register.html', views.register),
]

# Browser url access
http://127.0.0.1:8002/app01/register.html/

7. Virtual environment

In normal development, we will equip each project with an interpreter environment unique to the project.
In this environment, only the modules used by this project are not installed unless they are used.

linux: install whatever is missing

virtual environment
    Every time you create a virtual environment, it is like re-downloading a pure python interpreter.
    But don’t create too many virtual environments, as this will consume hard disk space.

Extension:
    Every project requires many modules, and the versions of each module may be different.
    So how do I install it? Do you look at each one and pretend to be different? ? ?

    During development, we will provide each project with a requirements.txt file
    All the modules or versions of the project are written in it.
    You only need to directly enter a command to install all modules and versions with one click

8. Django version differences

1. The django1.X routing layer uses the url method, while in the django2.Xhe3.X version, the routing layer uses the path method

url() first parameter supports regular expressions
The first parameter of path() does not support regular expressions. Whatever you write will match

If you are used to using path, we also provide you with another method.
    from django.urls import path, re_path
    from django.conf.urls import url

    re_path(r'^index/',index),
    url(r'^login/',login)

Tip: re_path in 2.X and 3.X is equivalent to url in 1.X

2. Although path does not support regular expressions, it internally supports five converters

path('index/<int:id>/',index)
# Convert the matched content in <int:id> to int type first, and then pass it to the index view function bound to it as keyword parameters.

def index(request,id):
print(id,type(id))
return HttpResponse('index')

str, matches a non-empty string except the path separator (/), which is the default form
int, matches positive integers, including 0.
slug, matches a string consisting of letters, numbers, dashes, and underscores.
uuid, matches formatted uuid, such as 075194d3-6885-417e-a8a8-6c931e272f00.
path, matches any non-empty string, including the path separator (/) (cannot be used?)

3. In addition to the default five converters, it also supports custom converters (understand)

class MonthConverter:
regex='\d{2}' #The attribute name must be regex

def to_python(self, value):
    return int(value)

def to_url(self, value):
    return value #The matching regex is two numbers, and the returned result must also be two numbers.


from django.urls import path,register_converter
from app01.path_converts import MonthConverter

# Register the converter first
register_converter(MonthConverter,'mon')

from app01 import views


urlpatterns = [
    path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]

4. The 1.X foreign keys in the model layer are deleted by cascade update by default, but in 2.X and 3.X, you need to manually configure the parameters yourself

models.ForeignKey(to='Publish')

models.ForeignKey(on_delete=models.CASCADE, on_update=models.CASCADE)

Supplement: The third parameter {} in the url

def url(regex, view, kwargs=None, name=None)
    ...

#urls.py
    url(r'^third_params/', views.third_params, {'username': 'egon'}, name='third_params'),

# views.py
    # def third_params(request, path): # Note: Pass parameters in keyword form
    def third_params(request, username):
        return HttpResponse(username) # username returns to the front-end page which is ego

9. Summary

# Route matching
    1. The first parameter of the url method in urls.py is regular
    2. By default, Django helps you enable the first matching failure. Append a slash / to the end of the matching path, and then redirect and re-traverse the loop to match again.
        Configuration: There is nothing in the default settings.py and you don’t need to add it yourself.
        APPEND_SLASH = False/True
    3. Define homepage url matching rules: url('^$', views.home)
    4. Define the last page url matching rules: url('', views.error)
        Tip: This method is generally not used for last pages. This method will invalidate jango's default APPEND_SLASH mechanism.
        We can then use exception handling as the content of the last page.

#Group
    # Unnamed group: no need to define a name
        Route writing: url('^index/(\d + )', views.index)
        Internal logic: The content matched by the regular expression in the brackets will be passed as a positional parameter to the view function index bound to it.
        View function:
            def index(request, xxx):
                pass

    # Named group: need to define a name
        Route writing: url('^index/(?P<year>\d + )', views.index)
        Internal logic: The content matched by the regular expression in the brackets will be passed as a keyword argument to the view function index bound to it.
        View function:
            def index(request, year):
                pass

    # Precautions:
        Also known as nameless groups, they cannot be mixed, but a single type can be used multiple times.
            url('^index/(\d + )/(\d + )/', views.index)
            url('^index/(?P<year>\d + )/(?P<year>\d + )/', views.index)

        If used multiple times, if it is an unnamed group, *args can be used in the view function to receive parameters.
            def index(request, *args):
                pass

        If it is used multiple times, if it is a named group, you can use **kwargs in the view function to receive parameters.
             def index(request, *kwargs):
                pass

        
# Reverse analysis
    # What is reverse parsing?
        Reverse parsing is to obtain a result through some methods. The result can directly access the corresponding URL to trigger the view function.

    # Use reverse analysis
        Step one: Give the routing and view functions an alias in urls.py (note: the alias cannot be repeated and must be unique)
            url('^index/', views.index, name='xxx')

        Part 2: Using reverse parsing
            Use at the template level:
                {% url 'xxx' %}
                <a href="{% url 'ooo' %}">111</a>
            Use in view layer:
                from django.shortcuts import reverse
                reverse('xxx')
                
                
# Reverse analysis of unknown and famous groups
    # Reverse analysis of unnamed groups
        # Routing layer definition
            url(r'^index/(\d + ), views.index, name='index')
        # Template layer usage:
            {% url 'index' number %}
        # View layer usage:
            from django.shortcuts import reverse
            def index(request, xxx)
                reverse('index', agrs=(xxx, )

    # Reverse parsing of famous groups
        # Routing layer definition
            url(r'^index/(?P<year>\d + ), views.index, name='index')
        # Template layer usage:
            {% url 'index' year=number %}
            {% url 'index' number %}
        # View layer usage:
            from django.shortcuts import reverse
            def index(request, year)
                reverse('index', kwagrs=({'year': number})
                reverse('index', agrs=(number, )

    # Unknown and named group reverse parsing usage scenarios
        Application: Generally used as primary key value
        Template layer application: reverse parsing by binding groups to the HTMl page, and when waiting for user submission, the primary key value of the user data is also carried.
            The view layer obtains the dynamic primary key values that are fed back from different operations, and models can use this primary key value to modify the current operation.
            Other data of the user for positioning.
        View layer application: redirection operation through reverse resolution

    # Note: The reverse parsing of unknown and named groups is the parsing of a single situation. For the parameters in reverse parsing,
        As long as the regular matching rules for routing are met.


# Route distribution:
    # Parts that should be distributed: templates, urls.py, models.py
    #Advantages of route distribution: Routes do not conflict between multiple applications, reducing the pressure on the overall route and the problem of poor maintenance. Decoupling can better realize group development
    # General routing settings:
        from django.conf.urls import url
        from django.conf.urls import include
        urlpatterns = [
            url(r'^admin/', admin.site.urls),
            # method one:
            from app01 import urls as app01_urls
            from app02 import urls as app02_urls
            url(r'^app01/', include(app01_urls)) # As long as the url prefix starts with app01, all will be handled by app01
            url(r'^app02/', include(app02_urls))

            # Method 2: Ultimate writing method recommended
            url(r'^app02/', include('app01.urls'))
            url(r'^app01/', include('app02.urls'))
        ]
        # Note: Do not use the format `^app01/$` in the main route, otherwise it will not be distributed to sub-routes.

    # Subrouting settings:
        # Configuration of urls.py in app01
            from django.conf.urls import url
            from app01 import views
            urlpatterns = [
                url(r'^index/', views.index),
            ]

        # Configuration of urls.py in app02
            from django.conf.urls import url
            from app02 import views
            urlpatterns = [
                url(r'^index/', views.index),
            ]


# namespace
    # Why do we need namespaces?
       When the same alias is set for routes in multiple applications and reverse parsing is used, the latter one will overwrite the previous one. If you want to trigger the previous view function, you will not be able to access it.

    # Configure the namespace in the total route
        from django.conf.urls import url
        from django.conf.urls import include
        urlpatterns = [
            url('^app01/', include('app01.urls', namespace='app01')),
            url('^app02/', include('app02.urls', namespace='app02')),
        ]

    # The matching rules in sub-routes are the same as the aliases
        # Configuration of urls.py in app01
            from django.conf.urls import url
            from app01 import views
            urlpatterns = [
                url('^reg/', views.reg, name='reg'),
            ]

        # Configuration of urls.py in app02
            from django.conf.urls import url
            from app02 import views
            urlpatterns = [
                url('^reg/', views.reg, name='reg'),
            ]

    # Subroutes use namespace syntax for reverse resolution
        # The view layer uses namespace syntax for reverse parsing
            reverse('app01:reg')
            reverse('app02:reg')

        # The template layer uses namespace syntax for reverse parsing
            {% url 'app01:reg' %}
            {% url 'app02:reg' %}

    # Supplement: In fact, as long as the names do not conflict, there is no need to use namespaces
        urlpatterns = [
            url('^reg/', views.reg, name='app01_reg'),
        ]
        urlpatterns = [
            url('^reg/', views.reg, name='app02_reg'),
        ]


# Pseudo-static
    Static web page: web page data written to death
    Pseudo-static: Disguise dynamic web pages as static web pages
    Function: Improve the SEO query strength of the website and increase the probability of collecting web pages. -> Crawler search engine
    The routing layer uses pseudo-static:
        urlpatterns = [
            url('^reg.html/', views.reg, name='app01_reg'),
        ]

# Virtual environment deployment

#django version difference
    1. The routing layer of django1.x version uses the url method, while the routing layer of django2.x and 3.x uses the path method.
        1.x version routing matching supports regular expressions
        Path in versions 2.x and 3.x does not support regular expressions. If you want to support regular expressions, import re_path. Although the url method is retained, its use is not recommended.

    2. Although django2.x and 3.x versions do not support regular expressions, they internally support five converters.
        from django.urls import path
        path('index/<int:id>', views.index)
        Convert the matched content in <int:id> to int type first, and then pass it to the index view function bound to it as keyword parameters.

    3. The converter supports customization
    4. The 1.x default cascade update and cascade delete in the model layer, while the 2.x and 3.x versions need to be specified manually.
        publish = models.ForeignKey(to='Publish', on_update=models.CASCADE, on_delete=models.CASCADE)