AssertionError: Mapowanie funkcji widoku zastępuje istniejącą funkcję punktu końcowego: main

86

Czy ktoś wie, dlaczego nie mogę zastąpić istniejącej funkcji punktu końcowego, jeśli mam dwie takie reguły adresu URL

app.add_url_rule('/',
                 view_func=Main.as_view('main'),
                 methods=["GET"])

app.add_url_rule('/<page>/',
                 view_func=Main.as_view('main'),
                 methods=["GET"])

Śledzenie:

Traceback (most recent call last): 
  File "demo.py", line 20, in <module> methods=["GET"]) 
  File ".../python2.6/site-packages/flask‌​/app.py", 
    line 62, in wrapper_func return f(self, *args, **kwargs) 
  File ".../python2.6/site-packages/flask‌​/app.py", 
    line 984, in add_url_rule 'existing endpoint function: %s' % endpoint)  
AssertionError: View function mapping is overwriting an existing endpoint 
    function: main
Kimmy
źródło
Pytasz, jak możesz lub dlaczego możesz?
Michael Davis
5
dlaczego to nie działa, postępuję zgodnie z tutorialem
Kimmy
1
Jeśli ktoś jest ciekawy, dlaczego Flask ma tę granicę unikalnych nazw widoków, zobacz moją odpowiedź na podobne pytanie: stackoverflow.com/a/47558985/4440675 Ta odpowiedź wyjaśnia, jaka logika kryje się za posiadaniem unikalnej nazwy dla każdej metody.
Amit Tripathi

Odpowiedzi:

67

Nazwy widoków muszą być unikalne, nawet jeśli wskazują na tę samą metodę widoku.

app.add_url_rule('/',
                 view_func=Main.as_view('main'),
                 methods = ['GET'])

app.add_url_rule('/<page>/',
                 view_func=Main.as_view('page'),
                 methods = ['GET'])
Michael Davis
źródło
Innymi słowy, .as_view($VIEW_NAME)wywołanie metody $ VIEW_NAME in powinno być przekazane jako unikalna nazwa ciągu.
Devy
109

Ten sam problem przytrafił mi się, gdy miałem więcej niż jedną funkcję API w module i próbowałem opakować każdą funkcję dwoma dekoratorami:

  1. @ app.route ()
  2. Mój niestandardowy dekorator @exception_handler

Dostałem ten sam wyjątek, ponieważ próbowałem zawinąć więcej niż jedną funkcję tymi dwoma dekoratorami:

@app.route("/path1")
@exception_handler
def func1():
    pass

@app.route("/path2")
@exception_handler
def func2():
    pass

W szczególności jest to spowodowane próbą zarejestrowania kilku funkcji z opakowaniem nazwy :

def exception_handler(func):
  def wrapper(*args, **kwargs):
    try:
        return func(*args, **kwargs)
    except Exception as e:
        error_code = getattr(e, "code", 500)
        logger.exception("Service exception: %s", e)
        r = dict_to_json({"message": e.message, "matches": e.message, "error_code": error_code})
        return Response(r, status=error_code, mimetype='application/json')
  return wrapper

Zmiana nazwy funkcji rozwiązała to za mnie ( wrapper .__ name__ = func .__ name__ ):

def exception_handler(func):
  def wrapper(*args, **kwargs):
    try:
        return func(*args, **kwargs)
    except Exception as e:
        error_code = getattr(e, "code", 500)
        logger.exception("Service exception: %s", e)
        r = dict_to_json({"message": e.message, "matches": e.message, "error_code": error_code})
        return Response(r, status=error_code, mimetype='application/json')
  # Renaming the function name:
  wrapper.__name__ = func.__name__
  return wrapper

Następnie udekorowano więcej niż jeden punkt końcowy.

Roei Bahumi
źródło
27
Możesz także użyć functools.wrapsdo zmiany nazwy funkcji.
ryannjohnson
4
Musiałem użyć wrapper.__name__zamiast wrapper.func_name. Być może jest to różnica między pythonem2 a pythonem3?
Rezonans
2
Na tym polegał problem w mojej kolbie restful API przy użyciu dekoratorów. wrapper.__name__ = func.__name__przed wywołaniem wrappera rozwiązało problem.
Tom Wojcik,
28

W przypadku użytkowników, którzy używają @ app.route, lepiej jest użyć klucza-argumentu endpoint, niż zmienić wartość, __name__jak stwierdził Roei Bahumi . Na jego przykładzie będzie:

@app.route("/path1", endpoint='func1')
@exception_handler
def func1():
    pass

@app.route("/path2", endpoint='func2')
@exception_handler
def func2():
    pass
Uri Shalit
źródło
8

Flask wymaga powiązania pojedynczej „funkcji widoku” z „punktem końcowym”. Dzwonisz Main.as_view('main')dwa razy, co tworzy dwie różne funkcje (dokładnie ta sama funkcjonalność, ale inna w sygnaturze pamięci). Krótka historia, po prostu powinieneś to zrobić

main_view_func = Main.as_view('main')

app.add_url_rule('/',
             view_func=main_view_func,
             methods=["GET"])

app.add_url_rule('/<page>/',
             view_func=main_view_func,
             methods=["GET"])
dtheodor
źródło
7

Chciałbym tylko dodać do tego rozwiązanie bardziej „szablonowe”.

def func_name(f):
    def wrap(*args, **kwargs):
        if condition:
            pass
        else:
            whatever you want
        return f(*args, **kwargs)
    wrap.__name__ = f.__name__
    return wrap

chciałbym tylko dodać naprawdę ciekawy artykuł „Demystifying Decorators”, który niedawno znalazłem: https://sumit-ghosh.com/articles/demystifying-decorators-python/

jestem Batmanem
źródło
4

Może się to również zdarzyć, gdy masz identyczne nazwy funkcji na różnych trasach.

Józefa
źródło
2

Jeśli uważasz, że masz unikalne nazwy punktów końcowych, a nadal jest podawany ten błąd, prawdopodobnie masz problem . Tak samo było ze mną.

Ten problem dotyczy kolby 0.10, jeśli masz tę samą wersję, a następnie wykonaj następujące czynności, aby się tego pozbyć:

sudo pip uninstall flask
sudo pip install flask=0.9
shrishinde
źródło
1

Istnieje poprawka dotycząca problemu z Flask # 570 wprowadzonego niedawno (flask 0.10), który powoduje zgłoszenie tego wyjątku.

Zobacz https://github.com/mitsuhiko/flask/issues/796

Więc jeśli przejdziesz do flask / app.py i zakomentujesz 4 linie 948..951, może to pomóc, dopóki problem nie zostanie w pełni rozwiązany w nowej wersji.

Różnica tej zmiany jest tutaj: http://github.com/mitsuhiko/flask/commit/661ee54bc2bc1ea0763ac9c226f8e14bb0beb5b1

yassen
źródło
3
Re: „dopóki problem nie zostanie w pełni rozwiązany w nowej wersji”: nie ma „problemu” do rozwiązania. Jak pokazała dyskusja problemowa, postawienie wyjątku w przypadku omawianego kodu jest oczekiwanym rezultatem. Jak wyjaśniono w zaakceptowanej odpowiedzi, punkty końcowe są w konflikcie. Jest to błąd w kodzie użytkownika Flaska, a NIE w samym Flasku.
Mark Hildreth
0

użyj flask 0.9 zamiast tego użyj następujących poleceń sudo pip uninstall flask

sudo pip install flask==0.9
Joshua Johns
źródło
0

Dodanie @wraps(f)powyżej funkcji opakowania rozwiązało mój problem.

def list_ownership(f):
    @wraps(f)
    def decorator(*args,**kwargs):
        return f(args,kwargs)
    return decorator
Matija Žiberna
źródło