AssertionError: o mapeamento da função de visualização está substituindo uma função de endpoint existente: principal

86

Alguém sabe por que não consigo substituir uma função de endpoint existente se tenho duas regras 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"])

Traceback:

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
fonte
Você está perguntando como pode ou por que pode?
Michael Davis
5
por que não funciona, estou seguindo um tutorial
Kimmy
1
Se alguém estiver curioso para saber por que o Flask tem esse limite de nomes de visualização exclusivos, veja minha resposta a uma pergunta semelhante: stackoverflow.com/a/47558985/4440675 Esta resposta explica a lógica por trás de ter um nome exclusivo para cada método.
Amit Tripathi

Respostas:

67

Os nomes de suas visualizações precisam ser únicos, mesmo que apontem para o mesmo método de visualização.

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
fonte
Em outras palavras, o $ VIEW_NAME na .as_view($VIEW_NAME)chamada do método deve ser passado como um nome de string exclusivo.
Devy
109

Esse mesmo problema aconteceu comigo quando eu tinha mais de uma função de API no módulo e tentei encapsular cada função com 2 decoradores:

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

Recebi a mesma exceção porque tentei encapsular mais de uma função com esses dois decoradores:

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

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

Especificamente, é causado pela tentativa de registrar algumas funções com o wrapper de 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

Alterar o nome da função resolveu para mim ( 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

Então, decorar mais de um ponto final funcionou.

Roei Bahumi
fonte
27
Você também pode usar functools.wrapspara alcançar a função de renomear.
ryannjohnson
4
Eu tive que usar em wrapper.__name__vez de wrapper.func_name. Talvez esta seja uma diferença entre python2 e python3?
Ressonância de
2
Esse era o problema na minha API de repouso do frasco usando decoradores. wrapper.__name__ = func.__name__antes de chamar wrapper resolveu o problema.
Tom Wojcik,
28

Para usuários que usam @ app.route, é melhor usar o argumento-chave em endpointvez de buscar o valor de __name__como Roei Bahumi declarou. Tomando seu exemplo será:

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

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

O Flask requer que você associe uma única 'função de visualização' a ​​um 'ponto final'. Você está chamando Main.as_view('main')duas vezes, o que cria duas funções diferentes (exatamente a mesma funcionalidade, mas diferentes na assinatura de memória). História curta, você deve simplesmente fazer

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

Gostaria apenas de adicionar a isso uma solução do tipo mais 'modelo'.

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

gostaria apenas de adicionar um artigo realmente interessante "Desmistificando decoradores" que encontrei recentemente: https://sumit-ghosh.com/articles/demystifying-decorators-python/

eu sou o Batman
fonte
4

Isso também pode acontecer quando você tem nomes de função idênticos em rotas diferentes.

Joseph
fonte
2

Se você acha que tem nomes de terminais exclusivos e ainda assim esse erro é fornecido, provavelmente você está enfrentando problemas . O mesmo foi o caso comigo.

Esse problema é com o frasco 0.10, caso você tenha a mesma versão, faça o seguinte para se livrar disso:

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

Há uma correção para o problema do Flask # 570 introduzido recentemente (flask 0,10) que faz com que essa exceção seja levantada.

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

Portanto, se você acessar flask / app.py e comentar as 4 linhas 948..951, isso pode ajudar até que o problema seja totalmente resolvido em uma nova versão.

A diferença dessa mudança está aqui: http://github.com/mitsuhiko/flask/commit/661ee54bc2bc1ea0763ac9c226f8e14bb0beb5b1

Yassen
fonte
3
Re: "até que o problema seja totalmente resolvido em uma nova versão": Não há "problema" a ser resolvido. Como a discussão do problema mostrou, o levantamento de uma exceção no caso do código em questão é o resultado esperado. Conforme explicado na resposta aceita, os terminais estão em conflito. Isso é uma falha no código do usuário do Flask, NÃO no próprio Flask.
Mark Hildreth
0

Seus nomes de visualização precisam ser exclusivos, mesmo se estiverem apontando para o mesmo método de visualização, ou você pode adicionar a partir de funções de importação wraps e usar @wraps https://docs.python.org/2/library/functools.html

Andriy Shchudlo
fonte
0

use o flask 0.9 em vez dos seguintes comandos sudo pip uninstall flask

sudo pip install flask==0.9
Joshua Johns
fonte
0

Adicionar @wraps(f)acima a função de invólucro resolveu meu problema.

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