AssertionError: la mappatura della funzione di visualizzazione sta sovrascrivendo una funzione endpoint esistente: main


86

Qualcuno sa perché non posso sovrascrivere una funzione endpoint esistente se ho due regole URL come questa

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"])

Rintracciare:

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

Stai chiedendo come puoi o perché puoi?
Michael Davis

5
perché non funziona sto seguendo un tutorial
Kimmy

1
Se qualcuno è curioso di sapere perché Flask ha questo limite di nomi di visualizzazione univoci, vedere la mia risposta a una domanda simile: stackoverflow.com/a/47558985/4440675 Questa risposta spiega quale logica sta dietro a un nome univoco per ogni metodo.
Amit Tripathi

Risposte:


67

I nomi delle viste devono essere univoci anche se puntano allo stesso metodo di visualizzazione.

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'])

In altre parole, la $ VIEW_NAME nella .as_view($VIEW_NAME)chiamata al metodo dovrebbe essere passata come un nome di stringa univoco.
Devy

112

Lo stesso problema è accaduto a me quando avevo più di una funzione API nel modulo e ho provato a racchiudere ogni funzione con 2 decoratori:

  1. @ app.route ()
  2. Il mio decoratore personalizzato @exception_handler

Ho ottenuto la stessa eccezione perché ho provato a racchiudere più di una funzione con quei due decoratori:

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

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

In particolare, è causato dal tentativo di registrare alcune funzioni con il wrapper del nome :

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

Cambiare il nome della funzione lo ha risolto per me ( 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

Quindi, decorare più di un endpoint ha funzionato.


28
È inoltre possibile utilizzare functools.wrapsper ottenere la funzione rinomina.
ryannjohnson

4
Ho dovuto usare wrapper.__name__invece di wrapper.func_name. Forse questa è una differenza tra python2 e python3?
Risonanza

2
Questo era il problema nella mia fiaschetta API riposante usando decoratori. wrapper.__name__ = func.__name__prima di chiamare wrapper ha risolto il problema.
Tom Wojcik

29

Per gli utenti che usano @ app.route è meglio usare l'argomento chiave endpointpiuttosto che cambiare il valore di __name__come ha affermato Roei Bahumi . Il suo esempio sarà:

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

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

9

Flask richiede di associare una singola "funzione di visualizzazione" a un "punto finale". Stai chiamando Main.as_view('main')due volte il che crea due funzioni diverse (esattamente la stessa funzionalità ma diversa nella firma della memoria). Racconto breve, dovresti semplicemente farlo

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"])

7

Vorrei solo aggiungere a questo una soluzione di tipo più "modello".

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

vorrei solo aggiungere un articolo davvero interessante "Demystifying Decorators" che ho trovato di recente: https://sumit-ghosh.com/articles/demystifying-decorators-python/


4

Ciò può accadere anche quando si hanno nomi di funzioni identici su percorsi diversi.


2

Se pensi di avere nomi di endpoint univoci e ancora viene fornito questo errore, probabilmente stai riscontrando un problema . Lo stesso è stato il caso con me.

Questo problema riguarda il flask 0.10 nel caso in cui tu abbia la stessa versione, quindi fai quanto segue per sbarazzarti di questo:

sudo pip uninstall flask
sudo pip install flask=0.9

1

Esiste una correzione per il problema Flask # 570 introdotto di recente (flask 0.10) che causa la generazione di questa eccezione.

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

Quindi, se vai su flask / app.py e commenta le 4 righe 948..951, questo può aiutare fino a quando il problema non viene risolto completamente in una nuova versione.

La differenza di tale modifica è qui: http://github.com/mitsuhiko/flask/commit/661ee54bc2bc1ea0763ac9c226f8e14bb0beb5b1


3
Ri: "fino a quando il problema non viene risolto completamente in una nuova versione": non ci sono "problemi" da risolvere. Come ha mostrato la discussione sul problema, la generazione di un'eccezione nel caso del codice nella domanda è il risultato atteso. Come spiegato nella risposta accettata, gli endpoint sono in conflitto. Questo è un errore nel codice dell'utente di Flask, NON in Flask stesso.
Mark Hildreth


0

usa il flask 0.9 invece usa i seguenti comandi sudo pip uninstall flask

sudo pip install flask==0.9

0

L'aggiunta @wraps(f)sopra la funzione wrapper ha risolto il mio problema.

def list_ownership(f):
    @wraps(f)
    def decorator(*args,**kwargs):
        return f(args,kwargs)
    return decorator
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.