AssertionError: la asignación de función de vista está sobrescribiendo una función de punto final existente: principal

86

¿Alguien sabe por qué no puedo sobrescribir una función de punto final existente si tengo dos reglas de URL como esta?

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

Rastrear:

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
fuente
¿Estás preguntando cómo puedes o por qué puedes?
Michael Davis
5
por qué no funciona Estoy siguiendo un tutorial
Kimmy
1
Si alguien tiene curiosidad por saber por qué Flask tiene este límite de nombres de vista únicos, vea mi respuesta a una pregunta similar: stackoverflow.com/a/47558985/4440675 Esta respuesta explica qué lógica hay detrás de tener un nombre único para cada método.
Amit Tripathi

Respuestas:

67

Los nombres de sus vistas deben ser únicos incluso si apuntan al mismo método de vista.

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
fuente
En otras palabras, la llamada al .as_view($VIEW_NAME)método $ VIEW_NAME in debe pasarse como un nombre de cadena único.
Devy
109

Este mismo problema me sucedió cuando tenía más de una función API en el módulo y traté de envolver cada función con 2 decoradores:

  1. @ app.route ()
  2. Mi decorador personalizado @exception_handler

Obtuve esta misma excepción porque intenté envolver más de una función con esos dos decoradores:

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

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

Específicamente, se produce al intentar registrar algunas funciones con el contenedor de nombres :

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

Cambiar el nombre de la función lo resolvió para mí ( envoltorio .__ nombre__ = func .__ nombre__ ):

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

Luego, la decoración de más de un punto final funcionó.

Roei Bahumi
fuente
27
También puede utilizar functools.wrapspara lograr el cambio de nombre de la función.
ryannjohnson
4
Tuve que usar en wrapper.__name__lugar de wrapper.func_name. ¿Quizás esta es una diferencia entre python2 y python3?
Resonancia
2
Ese era el problema en la API de mi matraz restful usando decoradores. wrapper.__name__ = func.__name__antes de llamar al contenedor resolvió el problema.
Tom Wojcik
28

Para los usuarios que usan @ app.route, es mejor usar el argumento clave en endpointlugar de cambiar el valor __name__como dijo Roei Bahumi . Tomando su ejemplo será:

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

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

Flask requiere que asocie una sola 'función de vista' con un 'punto final'. Está llamando Main.as_view('main')dos veces, lo que crea dos funciones diferentes (exactamente la misma funcionalidad pero diferente en la firma de memoria). Historia corta, simplemente debes hacer

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
fuente
7

Solo me gustaría agregar a esto una solución de tipo más 'plantilla'.

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

me gustaría agregar un artículo realmente interesante "Desmitificando a los decoradores" que encontré recientemente: https://sumit-ghosh.com/articles/demystifying-decorators-python/

soy Batman
fuente
4

Esto también puede suceder cuando tiene nombres de funciones idénticos en diferentes rutas.

Joseph
fuente
2

Si cree que tiene nombres de puntos finales únicos y aún se da este error, entonces probablemente esté enfrentando un problema . Lo mismo sucedió conmigo.

Este problema es con el frasco 0.10 en caso de que tenga la misma versión, haga lo siguiente para deshacerse de esto:

sudo pip uninstall flask
sudo pip install flask=0.9
Shrishinde
fuente
1

Hay una solución para el problema de Flask # 570 introducido recientemente (frasco 0.10) que hace que se genere esta excepción.

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

Entonces, si va a flask / app.py y comenta las 4 líneas 948..951, esto puede ayudar hasta que el problema se resuelva por completo en una nueva versión.

La diferencia de ese cambio está aquí: http://github.com/mitsuhiko/flask/commit/661ee54bc2bc1ea0763ac9c226f8e14bb0beb5b1

yassen
fuente
3
Re: "hasta que el problema se resuelva por completo en una nueva versión": no hay ningún "problema" que resolver. Como mostró la discusión del tema, el resultado esperado es el surgimiento de una excepción en el caso del código en la pregunta. Como se explica en la respuesta aceptada, los puntos finales están en conflicto. Esto es una falla en el código del usuario de Flask, NO en el propio Flask.
Mark Hildreth
0

use el matraz 0.9 en su lugar use los siguientes comandos sudo pip uninstall flask

sudo pip install flask==0.9
Joshua Johns
fuente
0

Agregar @wraps(f)arriba la función de contenedor resolvió mi problema.

def list_ownership(f):
    @wraps(f)
    def decorator(*args,**kwargs):
        return f(args,kwargs)
    return decorator
Matija Žiberna
fuente